HashMap源码分析JDK8-猿来绘Java-52

1. 构造器分析

使用HashMap时,经常用这样的语句(没有加入泛型):Map map = new HashMap();

    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    final float loadFactor;

    
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

 使用空参构造器时,底层只初始化了了loadFactor,初始化的值为 0.75,经过空参构造之后,无法存储key-value数据,接下来我们看下put方法。

2. put方法分析

    transient Node<K,V>[] table;

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

    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);
    
        // ...
    }

2.1 首次put时,内部的 HashMap 成员变量 table为null,底层创建了一个长度为16的Node类型数组 

 

 2.2 put数据时,发生hash冲突(先不考虑红黑树的情况)

2.3 如果hash值相同,但是跟链表第一个节点不同(也就是key不同,equals判断也不相同)

那么遍历链表上的每一个节点,如果没有找到key相同的节点,那么将数据插入到链表的末尾。

2.4 链表转换为红黑树

在putVal方法中,以上逻辑是将链表转换为红黑树,TREEIFY_THRESHOLD,也就是链表的长度即将达到阈值8的时候(实际上是7),会触发转换逻辑。我们继续看下面的逻辑:

 hash表中数据元素的个数小于64的时候,也不会将链表转换为红黑树。

所以,某个bucket上的链表转换为红黑树要同时满足两个条件:

1. 该hash bucket的链表长度>=8 (TREEIFY_THRESHOLD)

2. hash表的总长度>=64  (MIN_TREEIFY_CAPACITY)

对以上过程总结:

HashMap map = new HashMap():

在实例化以后,底层只是初始化了一个加载因子 this.loadFactor = DEFAULT_LOAD_FACTOR; ,该值为0.75,

首次map.put(key1,value1):底层初始化一个长度为 16 的 Node数组

多次put之后...

首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值(比较大)经过某种算法(哈希函数、散列函数)计算以后,得到在Node数组中的存放位置(也就是常说的hash bucket)。

        ※如果此位置上的数据为空,此时的key1-value1(Node)添加成功(需要判断是否满足转换为红黑树的条件)

        ※如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表或树形式存在)),比较key1和已经存在的一个或多个数据

的哈希值:

                √如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1(Entry)添加成功。(需要判断是否满足转换为红黑树的条件)

                √如果key1的哈希值和已经存在的某一个数据(key2-value2,Entry)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:(不同的对象可能存在hash值相同的情况)

                        ✨如果equals()返回false:此时key1-value1添加成功

                        ✨如果equals()返回true:使用value1替换value2

在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。

DEFAULT_INITIAL_CAPACITY : HashMap 的默认容量,16

DEFAULT_LOAD_FACTOR:HashMap 的默认加载因子:0.75,兼顾了数组的利用率,也兼顾了链表不能太长

threshold:扩容的临界值,= 容量 * 填充因子:16 * 0.75 => 12

TREEIFY_THRESHOLD: Bucket 中链表长度大于该默认值,转化为红黑树: 8

MIN_TREEIFY_CAPACITY:桶中的 Node 被树化时最小的 hash 表容量 : 64

猜你喜欢

转载自blog.csdn.net/asdfjklingok/article/details/118678170
今日推荐