HashMap resize()源码注释解读

分析版本是JDK 1.8, 包含红黑树的部分解析,几乎每一行都有注释,欢迎沟通交流。

/**
     * Initializes or doubles table size.  If null, allocates in
     * accord with initial capacity target held in field threshold.
     * Otherwise, because we are using power-of-two expansion, the
     * elements from each bin must either stay at same index, or move
     * with a power of two offset in the new table.
     *
     * @return the table
     */
    final Node<K,V>[] resize() {
     
        Node<K,V>[] oldTab = table;//获取存放数据的数组
        int oldCap = (oldTab == null) ? 0 : oldTab.length;//数组长度,也叫成为容量,不同于初始化容量initialCapacity < length(被拓展至2的幂次方 )
        /*左边两组是数组长度为16(2的4次方),右边两组是数组长度为15。两组的hashcode均为8和9,但是很明显,当它们和1110“与”的时候,产生了相同的结果,也就是说它们会定位到数组中的同一个位置上去,这就产生了碰撞,8和9会被放到同一个链表上,那么查询的时候就需要遍历这个链表,得到8或者9,这样就降低了查询的效率。同时,我们也可以发现,当数组长度为15的时候,hashcode的值会与14(1110)进行“与”,那么最后一位永远是0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!*/
        int oldThr = threshold;//阈值:容量*负载因子(0.75)
        int newCap, newThr = 0;
        if (oldCap > 0) {
     //只有非第一次扩容才会进来(第一次扩容在第一次put,table还没初始化,oldCap = 0)
            if (oldCap >= MAXIMUM_CAPACITY) {
     //已经扩容到最大容量
                threshold = Integer.MAX_VALUE;//调高阈值
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                    oldCap >= DEFAULT_INITIAL_CAPACITY)//扩容2倍,并且设置为新的容量
                //容量较小时 capacity * loadFactor造成的误差比较大,也就是oldCap对应的oldThr误差比较大,当oldCap<16时,不通过扩大2倍计算新阈值newThr
                //通过capacity * loadFactor计算还是位运算的权衡
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            // oldCap = 0 而 oldThr > 0,表示构造Map时带有阈值参数,oldThr初始化计算方法时tableSizeFor(initialCapacity)
            newCap = oldThr;//第一次初始化时oldThr实际含义是容量,在此是借壳初始化
        else {
                    // zero initial threshold signifies using defaults
            // oldCap = 0 而 oldThr <= 0 无参初始化,设置为默认值
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        //到此,newThr已经是最终值,但是newThr在带参初始化或者原容量比较小时,还未初始化
        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 = newTab;//使用一个新的数组代替已有的容量小的数组
        if (oldTab != null) {
     
            for (int j = 0; j < oldCap; ++j) {
     //分析每一个旧容器中的节点
                Node<K,V> e;
                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
                        //数组该位置是一个链表
                        // 之所以定义两个头两个尾对象,是由于链表中的元素的下标在扩容后,要么是原下标+oldCap,要么不变
                        //因为oldTab的元素下标是根据 hash(key) & (oldCap-1) 计算的,如果扩容后,计算下标是 hash(key) & (2*oldCap-1)
                        //扩容后的下标取决于hash(key)的二进制对应(2*oldCap-1)的高位是0还是1,而oldCap就是(2*oldCap-1)的高位
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        //遍历桶中的链表
                        do {
     
                            next = e.next;//记录链表下一个节点
                            if ((e.hash & oldCap) == 0) {
     
                                //与高位取与为0,表明下标没有改变,设置为低位链表loHead和loTail
                                if (loTail == null)
                                    //若尾节点为空,表明是第一个节点,所以只是为头节点,记录头节点
                                    loHead = e;
                                else
                                    //尾节点不为空,表明已经添加了节点,将其放在尾节点后面
                                    loTail.next = e;
                                loTail = e;//始终添加的节点是尾节点
                            }
                            else {
     
                                //与高位取与为1,表明下标会改变,设置为高位链表hiHead和hiTail
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);//去下一个节点,依次遍历完桶中的链表
                        if (loTail != null) {
     
                            //插入到和原容器相同的位置
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
     
                            //插入到更新后的位置
                            //由于0
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

红黑树操作:

 /**
         * Splits nodes in a tree bin into lower and upper tree bins,
         * or untreeifies if now too small. Called only from resize;
         * see above discussion about split bits and indices.
         *
         * @param map the map
         * @param tab the table for recording bin heads
         * @param index the index of the table being split
         * @param bit the bit of hash to split on
         */
        final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
     
            //map: HashMap对象
            //tab:新的Node[]
            //index:分析HashMap对象中的节点编号
            //bit: oldCap原来的HashMap对象容量大小
            TreeNode<K,V> b = this;//当前对象是原HashMap对象Node[index]这个节点
            // Relink into lo and hi lists, preserving order
            TreeNode<K,V> loHead = null, loTail = null;
            TreeNode<K,V> hiHead = null, hiTail = null;
            int lc = 0, hc = 0;
            for (TreeNode<K,V> e = b, next; e != null; e = next) {
     //双向链表遍历红黑树,拆分成为两个双向链表
                next = (TreeNode<K,V>)e.next;
                e.next = null;
                if ((e.hash & bit) == 0) {
     
                    //与高位取与为0,表明下标没有改变
                    if ((e.prev = loTail) == null)
                        loHead = e;
                    else
                        loTail.next = e;
                    loTail = e;
                    ++lc;
                }
                else {
     
                    //与高位取与为1,表明下标会改变
                    if ((e.prev = hiTail) == null)
                        hiHead = e;
                    else
                        hiTail.next = e;
                    hiTail = e;
                    ++hc;
                }
            }

            if (loHead != null) {
     
                if (lc <= UNTREEIFY_THRESHOLD)
                    //节点数不够红黑树个数,转化为链式的
                    tab[index] = loHead.untreeify(map);
                else {
     
                    tab[index] = loHead;//构建的红黑树双向链表赋值给新的Node[]
                    if (hiHead != null) // (else is already treeified)
                        //构建红黑树
                        loHead.treeify(tab);
                }
            }
            if (hiHead != null) {
     
                if (hc <= UNTREEIFY_THRESHOLD)
                    tab[index + bit] = hiHead.untreeify(map);
                else {
     
                    tab[index + bit] = hiHead;
                    if (loHead != null)
                        hiHead.treeify(tab);
                }
            }
        }

若拆分后红黑树节点数不够,会转化为链式,需要切换对象类型

/**
         * Returns a list of non-TreeNodes replacing those linked from
         * this node.
         */
        final Node<K,V> untreeify(HashMap<K,V> map) {
     
            Node<K,V> hd = null, tl = null;
            for (Node<K,V> q = this; q != null; q = q.next) {
     
                Node<K,V> p = map.replacementNode(q, null);
                if (tl == null)
                    hd = p;
                else
                    tl.next = p;
                tl = p;
            }
            return hd;
        }

若拆分后依旧能构成红黑树,需要将在双向链表结构基础上补充红黑树

 /**
         * Forms tree of the nodes linked from this node.
         * @return root of tree
         */
        final void treeify(Node<K,V>[] tab) {
     
            TreeNode<K,V> root = null;
            for (TreeNode<K,V> x = this, next; x != null; x = next) {
     //遍历构建的双向链表的红黑树
                next = (TreeNode<K,V>)x.next;//保留链式结构的下一个节点
                x.left = x.right = null;
                if (root == null) {
     
                    x.parent = null;
                    x.red = false;
                    root = x;//设置根节点
                }
                else {
     
                    K k = x.key;
                    int h = x.hash;
                    Class<?> kc = null;
                    for (TreeNode<K,V> p = root;;) {
     //在已有的红黑树结构上插入下的节点
                        int dir, ph;
                        K pk = p.key;
                        if ((ph = p.hash) > h)
                            dir = -1;
                        else if (ph < h)
                            dir = 1;
                        else if ((kc == null &&
                                (kc = comparableClassFor(k)) == null) ||
                                (dir = compareComparables(kc, k, pk)) == 0)
                            dir = tieBreakOrder(k, pk);

                        TreeNode<K,V> xp = p;
                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
     
                            x.parent = xp;
                            if (dir <= 0)
                                xp.left = x;
                            else
                                xp.right = x;
                            root = balanceInsertion(root, x);
                            break;
                        }
                    }
                }
            }
            moveRootToFront(tab, root);
        }

你可能感兴趣的:(Java,Java并发编程,HashMap,resize(),treeify(),untreeify())