hashMap 源码解读理解实现原理和hash冲突

hashMap 怎么说呢。 我的理解是 外表是一个set 数组,无序不重复 。 每个set元素是一个bean ,存着一对key value

看看代码吧

package test;

import java.util.HashMap;
import java.util.Map.Entry;

public class HashMaptest {

    public static void main(String[] args) {

        HashMap<String, String> map = new HashMap<String, String>();

        String aa = "张三";
        String bb = "李四";
        map.put(aa, "22");
        map.put(bb, "34234");
        map.put("张三", "223");

        System.out.println(aa.hashCode());
        System.out.println(bb.hashCode());

        for (Entry<String, String> entry : map.entrySet()) {

            System.out.println(entry.getKey());

            // System.out.println(entry.getValue());
        }

    }

}

打印的结果是:

774889
842061
李四
张三

可以看到, 虽然张三是先插进去的, 但是确在后面打印出来,说明这个数组不是有序的, 不是list;

虽然aa  的 hashcode=774889 大于bb 的, 肯定不是根据大小插入的,应该是把 得到的hashcode 取余 , 例如  6%7 = 6 , 9%(7+1)=1 ,6>1  ,所以9 在前面,这里为什么是  第一个除以7 ,第二个进来确实% 8呢;

因为hash算法是 这样的:

int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值

int index
= hash % Entry[].length;

Entry[index] = value;

这里看到  Entry[].length 一直在增加的;

所以就会遇到hash冲突, 总有碰到取余后一样的;

好了,举个例子:

比如 key="张三", hashcode=666 ,entry的size是 100   666%100=66  ;存在  66 位置上。  这个时候 key="李四" 要插入了, hashcode =6666 entry的size是 6600  6666%6600=66 , 这样 66 位置就hash冲突了;

这里和我代码写的覆盖不是一回事 。 代码的 key  相同,这里在特别说明下:可能有人说, 两个张三的hashcode 一样, 不同时候插入,entry 的 size不是不一样吗, 那就不会覆盖了? 这么想是错误的, 其实 map 插入的时候是先比较equals  的, 发现,咦 ,相同, 直接覆盖原值

:附上 hashMap的Put 方法源码:

   final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

可能比较难懂, 主要在这句:

if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }

已存在的key ,之而立是直接覆盖的。

时间: 2024-10-05 09:49:47

hashMap 源码解读理解实现原理和hash冲突的相关文章

HashMap源码阅读(2)- 碰撞(冲突)与扩容

上次在 HashMap源码阅读(1)- 初始值.数据结构.hash计算一文中描述了hashMap的初始大小,底层存储结构,以及哈希值计算和index计算,本文将接着上文,继续深入了解HashMap中hash碰撞和扩容问题 1)hash碰撞 谈hash,不得不提的当然是hash碰撞的问题,所谓hash碰撞,简单地说即由不同的key所计算出相同的hash值.笔者才疏学浅,所掌握的解决hash碰撞的方式有以下几种: 1.开放地址法: 当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测

深入理解JAVA集合系列:HashMap源码解读

初认HashMap 基于哈希表(即散列表)的Map接口的实现,此实现提供所有可选的映射操作,并允许使用null值和null键. HashMap继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口.且是不同步的,意味着它不是线程安全的. HashMap的数据结构 在java编程语言中,最基本的结构就两种,一个是数组,另一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的.HashMap也不例外,它是一个“链表的数组”的数据结构

HashMap 源码解读

这几天看了一下HashMap的源码,现此于大家分享! 1,HashMap的底层实现. 2,HashMap的扩容机制. 1,底层实现 public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); } 此为Ha

HashMap源码解读(jdk1.8)

一般在工作中,只有出问题的时候才会看看源码,很少有时间去单独看一下源码. 正好还没找到工作,朋友提了一句看看HashMap,所以花了点时间看了看. 对于翻源码这件事情,如果没有使用过,自己会像无头苍蝇一样,不知道从哪里开始. 对于HashMap,常用作key value容器,基本的使用方式,就是new 一个实例,put.get,或者通过keyset.entry遍历. Map map = new HashMap(); map.put(XX,XX);  map.get(XX,XX); map.key

HashMap源码解读

底层实质就是一个数组,数组里面装的是Entry类的对象,next指针指向下一个节点 package a.b.c.d.e.f; import java.io.*; import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Collection; import java.util.ConcurrentModificationExce

JAVA源码解读---HashMap目录扩展的奥秘

摘要:为了探索JAVA1.7源码中HashMap类数据的组织方法与目录扩展方法,本文通过对JAVA1.7源码中HashMap类源码的阅读与分析,得出结论:hashmap中存储数据的数据结构采用的是链表数组,目录是个数组,数组的成员是链表.冲突解决方法:典型的链地址法,冲突后,在链表头部插入数据.目录扩展方法:已二倍的方式扩展,一直到目录的最大上限.目录扩展的触发条件:装载因子的方式触发.从java中hashmap的实现可以看出,桶数据的组织方式并不是一种非常高效的方式.对检索效率不利.同时,数据

逐行解读HashMap源码

目录 一.写在前面 Java版本 二.HashMap官方说明 三.HashMap存储结构 四.HashMap静态属性 五.HashMap成员属性 六.HashMap构造方法 七.tableSizeFor(int cap)方法 八.hash(Object key)方法 九.桶下标计算公式 十.put(K key, V value)方法 十一.resize()方法 十二.get(Object key)方法 十三.HashMap序列化与反序列化 十四.treeifyBin(Node[] tab, in

jdk1.8.0_45源码解读——HashMap的实现

jdk1.8.0_45源码解读——HashMap的实现 一.HashMap概述 HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作.存储的是<key,value>对的映射,允许多个null值和一个null键.但此类不保证映射的顺序,特别是它不保证该顺序恒久不变.  除了HashMap是非同步以及允许使用null外,HashMap 类与 Hashtable大致相同. 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能.迭代col

十分钟深入理解HashMap源码

十分钟就要深入理解HashMap源码,看完你能懂?我觉得得再多看一分钟,才能完全掌握! 终于来到比较复杂的HashMap,由于内部的变量,内部类,方法都比较多,没法像ArrayList那样直接平铺开来说,因此准备从几个具体的角度来切入. 桶结构 HashMap的每个存储位置,又叫做一个桶,当一个Key&Value进入map的时候,依据它的hash值分配一个桶来存储. 看一下桶的定义:table就是所谓的桶结构,说白了就是一个节点数组. transient Node<K,V>[] tab