HashMap的扩容机制解释--resize()

HashMap 的resize方法解释

final Node<K,V>[] resize() {
		//1.table赋值给一个临时变量oldTab
        Node<K,V>[] oldTab = table;
		//2.记录oldTable的容量为oldCap,如果oldTab是null,oldCap=0,否则就为oldCap的大小,第一put值的时候为null
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
		//3.把oldTab的扩充阈值记录为oldThr
        int oldThr = threshold;
		//4.初始化新的容量和扩充阈值为0
        int newCap, newThr = 0;
		//5.进行oldCap的容量判断
		//第二次进入
        if (oldCap > 0) {
			//首先判断oldCap的是不是大于HashMap允许的最大容量,如果是就把HashMap的扩充阈值直接赋值为int的最大值,并且立即返回旧的table,不经行下面的操作
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
			//让newCap是原来的2倍,判断是不是大于最大的容量,然后判断就的容量是不是大于默认的初始化容量,然后让newThr变为原来的两倍
			// 这里除了最后一次扩容,应该总是能成功的,当时最后一次扩容大于最大的初始化容量的时候newCap,然后newThr还是0
            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;
		//第一put值的时候选择
        else {               // zero initial threshold signifies using defaults
			//初始化容量为默认容量16
            newCap = DEFAULT_INITIAL_CAPACITY;
			//初始化默认扩容阈值为12
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
		//第一次扩容完成newThr =12
		//当扩容的newCap大于最大的初始化容量的时候newThr就会为0
        if (newThr == 0) {
			//计算新的容量乘以装载因子的值
            float ft = (float)newCap * loadFactor;
			//再newThr的为0的时候newCap已经大于最大的容量,那么newThr就会为int的最大值2^31 -1
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
		//给扩充阈值赋值
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
		//初始化HashMap的桶,第一次为16
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
		//将newTab赋值给HashMap中的table元素
        table = newTab;
		//第一次oldTabl == null 不经行元素的移动
		//除了第一次后面的每次扩容都需要经行这步
        if (oldTab != null) {
			//从数组的头开始遍历整个oldTable
            for (int j = 0; j < oldCap; ++j) {
				//新增临时遍历e
                Node<K,V> e;
				//初始化e,从oldTable的最低位开始,不为null就经行下一步,null则跳过,继续循环
                if ((e = oldTab[j]) != null) {
					//将oldTb[j] 变为null,这里应该是辅助GC,因为我们已经有临时变量e
                    oldTab[j] = null;
					//这里判断是否为链,没有链则让他于新的newCap的新位置
                    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
					//这个时候e为一个链表
						// 初始化低位的头指针和尾指针 loHead,loTail
                        Node<K,V> loHead = null, loTail = null;
						//初始化高位的头指针和尾指针
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
						//循环e知道e为null
						//获取key存储的位置是使用容量-1和hash值做与运算 例子比如容量是16,则和hash做与运算的就是15,化为二进制则是1111,16的二进制是10000
						//现在扩容一倍,则如果经行hash则新一轮的与运算则是32-1,就是和31做与运算,31的二进制我们可以发现是11111,就是再原来的old值上增加1个1
						//所以他和原来的16做与运算 就是二进制的16 10000,然后看是否为0,为0我们可以发现就是0 ???? 和11111做与运算不管后面是什么他还在原来的位置
						//当时1????的时候,就和11111就是再原来的上面加了16
                        do {
                            next = e.next;
							// e.hash 和原来的容量做与运算 oldCap假如为16则oldCap二进制10000 
                            if ((e.hash & oldCap) == 0) {
								//链表第一次进入loTail 为null
								//第二次进入 loTail !=null
                                if (loTail == null)
									//将loHead 为e
									//解释这里只给e赋值一次,同下面同理,假如这个链有7个节点,第一个元素和原来的容量与是0,就把lohead变为e这个链
									//继续向后遍历遇到不为0就将整个赋给高位,这样这个lohead和hihead最多就4种情况,第一全部与原来的容量都是0,或者都为1,或者两者都有
									//两者都有我们可以分为两种情况,与操作为0的元素排在链表的头,和与操作为1的排到了头
									//0为头,则到时候lohead就是整个链,hiHead就为第一个节点与为1的后面所有链
									//这样其实对查询是没有任何影响的,并且简化了操作
									//最后就是,loHead和hiHead有一个就是全部链表的长度,另外一个则是链表出现的哪位开始截取的
                                    loHead = e;
                                else
									//loTail的下一个节点为e
                                    loTail.next = e;
								// 最后将loTail 也置为e
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
									//这里也是把e变为整个链,没有对下一个节点的操作
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
						//判断给新的table赋值
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
		//返回新的table
        return newTab;
    }

发布了6 篇原创文章 · 获赞 0 · 访问量 101

猜你喜欢

转载自blog.csdn.net/Chenpeng02/article/details/105108170