(java 8)HashMap-resize()解读

final Node<K,V>[] resize() {
        // 扩容前的数组
        Node<K,V>[] oldTab = table;
        // 扩容前的数组的大小和阈值
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        // 预定义:新数组的大小和阈值
        int newCap, newThr = 0;
        if (oldCap > 0) {
        // 数组最大容量不能超过MAXIMUM_CAPACITY,超过了就不会再扩容
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            // 否则扩大为当前容量的两倍,并且也是不能超过MAXIMUM_CAPACITY
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        // 当前数组没有数据,使用初始化的值
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
        // 如果初始化的值,为0,则使用默认的出初始化容量
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        // 阈值也是不能超过MAXIMUM_CAPACITY
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
            // 开始扩容,将新的容量赋值给table
        table = newTab;
        // 之前有数据,这里需要转移,重点是里面的do循环
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                // 这里设置了当前数组的为null,可以防止其他线程(也在扩容)进入if ((e = oldTab[j]) != null)这个代码块吧,猜测
                    oldTab[j] = null;
                    // 这个数组只保存了一个数据时,扩容之后肯定还是在新数组的某个桶下的第一个元素,为什么?
                    // 扩容的时候是链表顺序遍历的,头结点肯定先被遍历到,新数组的位置还是在头结点位置
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                    // 红黑树相关的处理
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                    // loHead 表示老值,老值的意思是扩容后,该链表中计算出索引位置不变的元素
                    // hiHead 表示新值,新值的意思是扩容后,计算出索引位置发生变化的元素
                    // 举个例子,数组大小是 8 ,在数组索引位置是 1 的地方挂着一个链表,链表有两个值,两个值的 hashcode 分别是是9和33。
                    // 当数组发生扩容时,新数组的大小是 16,此时 hashcode 是 33 的值计算出来的数组索引位置仍然是 1,我们称为老值
                    // hashcode 是 9 的值计算出来的数组索引位置是 9,就发生了变化,我们称为新值。
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            // 这个的效果就是,假如一开始的容量是8,现在扩容到16,那么hash是{0~7}值与8都是0,大于等于8的hash与8都是8(不等于0)
                            // &运算 可以理解为,两个 "101010" 这种字符串 取"1"交集的运算
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                    // 最后再把新值和老值的链表,才给数组赋值
                    // java 7 是在 while 循环里面,单个计算好数组索引位置后,单个的插入数组中,在多线程情况下,会有成环问题
                    // java 8 是等链表整个 while 循环结束后,才给数组赋值,所以多线程情况下,也不会成环
                        if (loTail != null) {
                            loTail.next = null;
                // 链表的头结点 newTab[j]
= loHead; } if (hiTail != null) { hiTail.next = null;
                // 链表的头结点 newTab[j
+ oldCap] = hiHead; } } } } } return newTab; }

 总结:

1.数组最大容量不能超过MAXIMUM_CAPACITY,超过了就不会再扩容

2.扩大为当前容量的两倍,并且也是不能超过MAXIMUM_CAPACITY

3.扩容阈值也是不能超过MAXIMUM_CAPACITY

4.java 7 在 while 循环里面,单个计算好数组索引位置后,单个的插入数组中,在多线程情况下,会有成环问题

java 8 是等链表整个 while 循环结束后,才给数组赋值,所以多线程情况下,也不会成环

posted @ 2019-12-31 13:58  Enast  阅读(337)  评论(0编辑  收藏  举报