final Node<K,V>[] resize()
方法的作用是进行哈希表容量的初始化或扩容,在向HashMap插入数据的时候会被调用到,具体实际的操作可能有如下几种情况:
- 直接复制(只有一个节点的桶)
- 链表拆分(有多个节点的桶)
- 链表转红黑树 或 红黑树恢复链表
实际代码及详细注释如下:
/**
* 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) { // 如果旧容量大于0,正常使用情况下
// 如果旧容量大于最大容量值,新容量等于Integer最大值,阈值不变,返回旧哈希表。
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 如果旧容量没超过最大容量值,新容量=旧容量×2(左移1位相当于×2),
// 然后判断新容量是否小于最大容量并且旧容量大于初始容量
// 如果都符合,新阈值=旧阈值×2(左移1位相当于×2)
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
// 如果旧阈值大于0,新的容量等于旧的阈值,
// 也就是:指定阈值创建对象但是却没用过此HashMap的时候
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
// 否则(说明旧容量和旧阈值都不大于0,也就是HashMap没有初始化过)
// 那么就是执行的初始化操作,容量和阈值均采用默认值。
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) { // 新阈值为0,也就是和上面的else if 情况一样的时候
// 如果新的容量和新容量×负载因子 均小于最大容量 新阈值=新容量×负载因子
// 否则新阈值为Integer最大值。
// 这里 新容量×负载因子大于最大容量判断在后,所以应该是为了避免自定义负载因子大于1的情况
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 = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) { // 遍历获取、使用每一个桶
Node<K,V> e;
if ((e = oldTab[j]) != null) { // 如果桶不为空,e为该桶的头结点
oldTab[j] = null;
if (e.next == null) // 如果桶只有一个节点,直接赋值给新的HashMap的桶
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode) // 链表转红黑树 或 红黑树恢复链表
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order // 如果是链表,对链表进行拆分, lo和hi分别代表新旧链表,扩容后重新按照哈希值找Node应放的桶
Node<K,V> loHead = null, loTail = null; // 链表拆分会放到lo和hi两个链表中,最终lo放到原索引的桶的位置
Node<K,V> hiHead = null, hiTail = null; // 链表拆分会放到lo和hi两个链表中,最终hi放到扩容出来的新索引的桶的对应位置
Node<K,V> next; // 临时变量存储下一个节点
do {
next = e.next; // 给next赋值下一个节点
// 因为扩容是<<1,相当于×2,所以会把一个桶中的节点放到两个桶中
// 正常获取桶位置是与上cap-1,此时与上cap就会将一种结果变成两种结果
// 就会把同一个桶中的节点分为两批,分别放入两个桶中,
// 不理解可以自己算一个,比如cap=4,有几个key哈希值是1、5、9、13,
// 和3(cap-1)与一下,再和4(cap)与一下,看结果就懂了。
if ((e.hash & oldCap) == 0) { // 尾插法插入到lo链表
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else { // 尾插法插入到hi链表
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) { // 将lo链表放到原来桶对应索引位置
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) { // 将hi链表放到新扩容出来的桶的对应位置
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab; // 返回新哈希表
}