HashMap源码分析(四)

HashMap源码分析(四)

JDK1.8

从上几篇HashMap源码分析(一)HashMap源码分析(二)HashMap源码分析(三)我们了解了HashMap基本的数据结构,也了解了怎么生成链表的,也知道怎么动态扩容了,但这几片有几处我们当时是忽略的,比如当生成的链表个数大于8的时候。接下来我们就看一下大于8,具体是做哪些操作。

先看测试代码:

//为了测试,编写一个实体类,重写了hascode,所有的实例hashcode都是一样的
class Demo{
    String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Demo(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Demo)) return false;
        Demo demo = (Demo) o;
        return Objects.equals(getName(), demo.getName());
    }

    @Override
    public int hashCode() {

        return 123;
    }
}


 @Test
    public void test(){
        HashMap hashMap = new HashMap();
        hashMap.put(new Demo("1"),"1");
        hashMap.put(new Demo("2"),"1");
        hashMap.put(new Demo("3"),"1");
        hashMap.put(new Demo("4"),"1");
        hashMap.put(new Demo("5"),"1");
        hashMap.put(new Demo("6"),"1");
        hashMap.put(new Demo("7"),"1");
        hashMap.put(new Demo("8"),"1");
        hashMap.put(new Demo("9"),"1");
    }

前面8个put操作我们都分析过了,我们就看第九个操作,为什么是从第九个开始,看一下源码:

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 //TREEIFY_THRESHOLD
            treeifyBin(tab, hash);
        break;
    }
    if (e.hash == hash &&
        ((k = e.key) == key || (key != null && key.equals(k))))
        break;
    p = e;
}

可以看到当链表的长度大于8的时候(TREEIFY_THRESHOLD,从9的时候开始),开始调用**treeifyBin()**方法。

/** * Replaces all linked nodes in bin at index for given hash unless * table is too small, in which case resizes instead. 对于给定的哈希,在索引处替换bin中的所有链接节点,除非表太小,否则改为调整大小。 */
final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; Node<K,V> e;
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) //64
        resize();
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        TreeNode<K,V> hd = null, tl = null;
        do {
            TreeNode<K,V> p = replacementTreeNode(e, null);
            if (tl == null)
                hd = p;
            else {
                p.prev = tl;
                tl.next = p;
            }
            tl = p;
        } while ((e = e.next) != null);
        if ((tab[index] = hd) != null)
            hd.treeify(tab);
    }
}

从注释可以看出,在数组长度小于64的时候,就是相当于扩容。

下面我们为了测试,在初始化的时候传入数组长度。

HashMap hashMap = new HashMap(64);

下面通过图来讲解:

《HashMap源码分析(四)》

上图是没有转化时候的数据结构。

《HashMap源码分析(四)》

上面就是转化程红黑树的过程,调用的treefiyBin(),然后里面有调用了treefiy().

好吧,红黑树我就暂时分析到这,说实在,分析完我也不是太懂红黑树的原理。。。记住hashMap内部是数组+链表+红黑树组成的就行了。

在网上看到下图,总结的不错,分享一下,正好可以结合我之前的博客看一下。

《HashMap源码分析(四)》

(图片来源:https://blog.csdn.net/lianhuazy167/article/details/66967698)

HashMap jdk1.7和jdk1.8的区别

JDK1.7JDK1.8
扩容的顺序先扩容,再插入先插入,再扩容
扩容的机制计算链表中每一个元素的下标判断新增的bit位是否是1,是0就是原下标,是1就是原下标+原数组长度
红黑树链表长度大于8是,转换成红黑树
    原文作者:Coder_Qiang
    原文地址: https://blog.csdn.net/Coder_Qiang/article/details/84345246
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞