JDK源码学习之HashMap扩容

HashMap的扩容函数

Node<K,V>[] resize()

阅读了HashMap的源码,对其扩容函数做了详细注释(基于JDK1.8)

/**
 * HashMap扩容函数resize
 */
final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;//原来的hash桶数组
    int oldCap = (oldTab == null) ? 0 : oldTab.length;//原来的哈希桶数组容量(数组长度)
    int oldThr = threshold;//原来的扩容阈值
    int newCap, newThr = 0;//新的容量和新的阈值
    if (oldCap > 0) {//如果原来的容量大于0(也就是非初始化的时候)
        if (oldCap >= MAXIMUM_CAPACITY) {//如果大于最大值,那么就没法再扩容了,只修改下扩容阈值为最大值,
            threshold = Integer.MAX_VALUE;//
            return oldTab;//然后返回
        }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&//如果没有到最大值,那么新的容量乘2
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold//新的扩容阈值乘2
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {//假如阈值为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) {//假oldTab不为null
        for (int j = 0; j < oldCap; ++j) {//遍历哈希桶数组
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {//取出哈希桶数组的元素,如果这哈希桶中不为空
                oldTab[j] = null;//将老哈希桶数组中的这个元素置为null.释放内存
                if (e.next == null)//如果这个桶里面只有一个元素
                    newTab[e.hash & (newCap - 1)] = e;//直接放到新哈希桶数组对应的老哈希桶位置,e.hash & (newCap - 1)的值就是新的位置,因为桶大小为2的n次幂,所以这里等于做了取模运算。
                else if (e instanceof TreeNode)//检查是不是红黑树,如果是红黑树,那就插到红黑树里面
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order//把老哈希桶中冲突的元素的头指针和尾指针找出来
                    Node<K,V> loHead = null, loTail = null;//需要移动到 +oldCap 位置的节点,也就是不移动的节点
                    Node<K,V> hiHead = null, hiTail = null;//需要移动到 现在位置+oldCap 位置的节点 
                    Node<K,V> next;
                    //判断是不是应该移动,这里不需要重新计算hash值来映射到新的数组位置
                    //因为扩容了2倍,所以,取模运算后(这里实际没有取模,而是用位运算替代了),原来哈希桶数组位置不是在原位置就是向左移动一位
                    //只需要比较哈希值原来位的上面一位是不是1就知道了,如果是1,那么位置向左移动一位(现在位置+oldCap)否则不用移动
                    //比较的方法是新的哈希桶新的容量大小和哈希值按位与:e.hash & oldCap
                    //然后将哈希桶数组中当前节点分为两类,一类是需要移动的,一类是不需要移动的(相对位置)
                    //最后,把分好的两个链表放到新table的原来的位置和新table的 现在位置+oldCap 位置
                    do {
                        next = e.next;
                        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);
                    if (loTail != null) {//添加到新哈希桶的原位置
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;//添加到新哈希桶的 j+oldCap位置
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}
发布了28 篇原创文章 · 获赞 9 · 访问量 2424

猜你喜欢

转载自blog.csdn.net/qq_23594799/article/details/104361093