JDK13-HashMap-resize源码解析

JDK13-HashMap-resize源码解析

resize是重新散列,所以要在现在容量和阈值的基础上获取新的容量和阈值,函数首先进行了变量定义

final HashMap.Node<K,V>[] resize() {//resize函数的返回值是一个Node数组
        HashMap.Node<K,V>[] oldTab = table;//transient Node<K,V>[] table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;//原容量
        int oldThr = threshold;//之前求出的threshold赋值给原阈值
        int newCap, newThr = 0;//resize之后的新容量和阈值

下面一段代码就是根据原容量和阈值计算新容量和阈值的方法

if (oldCap > 0) {//原Node数组非空,执行扩容操作
            if (oldCap >= MAXIMUM_CAPACITY) {//MAXIMUM_CAPACITY=1 << 30
                threshold = Integer.MAX_VALUE;//MAX_VALUE=1<<31 - 1
                return oldTab;//表示已经到达最大容量,不能再扩容了
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                    oldCap >= DEFAULT_INITIAL_CAPACITY)//DEFAULT_INITIAL_CAPACITY= 1<<4 = 16
                newThr = oldThr << 1; 
                //左移一位是double,newCap和newThr都变成double
        }
        //下面oldCap = 0,对table进行初始化,确定初始的容量和阈值
        else if (oldThr > 0)
            newCap = oldThr;//原来求的阈值赋值给新容量
        else {
            newCap = DEFAULT_INITIAL_CAPACITY;//16
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//16*0.75=12
        }
        if (newThr == 0) {//说明是(else if (oldThr > 0) newCap = oldThr;//原来求的阈值赋值给新容量)这种情况
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                    (int)ft : Integer.MAX_VALUE);
        }
        //将 新容量*输入的loadFactor 的值赋给新阈值,完成初始化
        threshold = newThr;

确定新的table的容量和阈值之后,如果原来已保存数据,需要对原来的数据重新散列,使分布均匀,这是JDK8之后对JDK7的优化,不会使链表越来越长

HashMap.Node<K,V>[] newTab = (HashMap.Node<K,V>[])new Node[newCap];//产生一个新的Node数组
        table = newTab;//这一句说明只要初始化一次之后,table就不可能为0
        if (oldTab != null) {//只有在非初始化的情况下,才重新分配数据
            for (int j = 0; j < oldCap; ++j) {//++j表示先++,再赋值
                HashMap.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)//e属于树节点
                        ((HashMap.TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { //如果当前节点在链表方向上还有节点
                            //需要把链表上的所有节点重新分配
                        HashMap.Node<K,V> loHead = null, loTail = null;
                        HashMap.Node<K,V> hiHead = null, hiTail = null;
                        HashMap.Node<K,V> next;
                        do {
                        //解析见下文
                        } while ((e = next) != null);
                        if (loTail != null) {
                        //解析见下文
                        }
                        if (hiTail != null) {
                        //解析见下文
                        }
                    }
                }
            }
        }
        return newTab;

do···while函数就是把一个桶对应的链表上的每一个节点,利用e.hash & 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);

e.hash & oldCap 其实是取出e.hash上的一个标志位,因为oldCap总是2的某次方,所以oldCap用位表示是00xx10xx0的形式。
在e.hash中,oldCap中1对应的那一位,e.hash可能为0,可能为1

如果这一位是0,那么e.hash & (oldCap-1)就等于e.hash & (newCap-1)

如果这一位是1,那么于e.hash & (newCap-1)=e.hash & (oldCap-1)+oldCap

通过这种方法,把链表上的节点分到两个新的链表中,一个是lo链表,一个是hi链表

当这个链表上所有的节点都被分配完之后,再把新生成的链表放进newTab的桶里

if (loTail != null) {
    loTail.next = null;//loTail和hiTail 的值使用之后,会把loTail和hiTail 清零
    newTab[j] = loHead;//把loHead对应的新的链表放在原来的桶里
 }
if (hiTail != null) {
    hiTail .next = null;
    newTab[j + oldCap] = hiHead;//把hiHead对应的新链表放在偏移oldCap的桶里
 }

使用(e.hash & oldCap) == 0来进行分配的原因:可以参考
https://segmentfault.com/a/1190000015812438?utm_source=tag-newest

原创文章 64 获赞 27 访问量 9438

猜你喜欢

转载自blog.csdn.net/weixin_44893585/article/details/103638977