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) {
// 数组最大容量不能超过MAXIMUM_CAPACITY,超过了就不会再扩容
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 否则扩大为当前容量的两倍,并且也是不能超过MAXIMUM_CAPACITY
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 { // zero initial threshold signifies using defaults
// 如果初始化的值,为0,则使用默认的出初始化容量
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
// 阈值也是不能超过MAXIMUM_CAPACITY
if (newThr == 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
table = newTab;
// 之前有数据,这里需要转移,重点是里面的do循环
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
// 这里设置了当前数组的为null,可以防止其他线程(也在扩容)进入if ((e = oldTab[j]) != null)这个代码块吧,猜测
oldTab[j] = null;
// 这个数组只保存了一个数据时,扩容之后肯定还是在新数组的某个桶下的第一个元素,为什么?
// 扩容的时候是链表顺序遍历的,头结点肯定先被遍历到,新数组的位置还是在头结点位置
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
// loHead 表示老值,老值的意思是扩容后,该链表中计算出索引位置不变的元素
// hiHead 表示新值,新值的意思是扩容后,计算出索引位置发生变化的元素
// 举个例子,数组大小是 8 ,在数组索引位置是 1 的地方挂着一个链表,链表有两个值,两个值的 hashcode 分别是是9和33。
// 当数组发生扩容时,新数组的大小是 16,此时 hashcode 是 33 的值计算出来的数组索引位置仍然是 1,我们称为老值
// hashcode 是 9 的值计算出来的数组索引位置是 9,就发生了变化,我们称为新值。
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
// 这个的效果就是,假如一开始的容量是8,现在扩容到16,那么hash是{0~7}值与8都是0,大于等于8的hash与8都是8(不等于0)
// &运算 可以理解为,两个 "101010" 这种字符串 取"1"交集的运算
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);
// 最后再把新值和老值的链表,才给数组赋值
// java 7 是在 while 循环里面,单个计算好数组索引位置后,单个的插入数组中,在多线程情况下,会有成环问题
// java 8 是等链表整个 while 循环结束后,才给数组赋值,所以多线程情况下,也不会成环
if (loTail != null) {
loTail.next = null;
// 链表的头结点
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
// 链表的头结点
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
总结:
1.数组最大容量不能超过MAXIMUM_CAPACITY,超过了就不会再扩容
2.扩大为当前容量的两倍,并且也是不能超过MAXIMUM_CAPACITY
3.扩容阈值也是不能超过MAXIMUM_CAPACITY
4.java 7 在 while 循环里面,单个计算好数组索引位置后,单个的插入数组中,在多线程情况下,会有成环问题
java 8 是等链表整个 while 循环结束后,才给数组赋值,所以多线程情况下,也不会成环