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;
}