resize()源码如下:
/**
* 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;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
//如果原来的容量已经大于了最大容量,则把threshold设置成int的最大值
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
//否则,新的容量,扩充为原来的一杯oldCap << 1 向左位移1为,相当于oldCap * 2
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;
//如果走下面这个else,则说明是第一次put元素,也是第一次进行扩展
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
//扩容 = 扩容因子 * 容量,DEFAULT_INITIAL_CAPACITY 默认是16,DEFAULT_LOAD_FACTOR默认是0.75
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
//newThr如果==0,说明不是第一次进行扩容了,因为如果是第一次,newThr在上面的代码中,已经赋值了
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
//判读ft是否小于MAXIMUM_CAPACITY,如果小于,则将newThr=ft
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
//将新的阈值赋值给threshold
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;
//如果列表里面只有一个元素,直接将e的key的hash与新容量重新计算下标
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 {
/*
这一块是处理原来的HashMap冲突的,因为容量扩容了
原来通过 & 运算出来的hash冲突的key,与新的容量 & 可能位置会发生改变
比如之前的冲突的两个key (k1,k2)的hash 分别为 1101,11101
初始化默认的容量是16 二进制就是 10000, 16-1的二进制就是 1111 与key进行& 运算结果都是 1101,但是与11111(36-1)进行 一个是1101 另一是11101
所以需要重新计算下标,通过与oldCap 进行 & 运算 分为低位和高位,因为和oldCap &运算只有两种结果,一种是0 另一种是oldCap
将结果=0的设置成低位,将等于oldCap的保存到高位,
低位的hash值肯定小于oldCap,所以下标还是在原来的位置
高位的hash一定大于oldCap,在二进制最左至少会多一个1(有可能还有前面还有其他二进制数字,但运算结果都是一样的)计算正好是原来位置加上新的容量
*/
/ preserve order
//低位位链表头和尾
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) {
//如果loTail==null说明是第一个元素,把这个元素赋值给loHead
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;
}