- 没有考虑树的情况,只考虑链表的情况
- 建议先自己看一下resize源码,琢磨之后再来看博客
- 这个方法中包含了对哈希表的扩容(或者初始化),把原来哈希表中的元素搬到新哈希表中去,把原来链表上的各个元素也搬到新的哈希表去
Node<K,V>[] oldTab = table;
// 拿到原来的哈希表,原来的哈希表长度就是旧容量
int oldCap = (oldTab == null) ? 0 : oldTab.length;
// 拿到原来的阈值
int oldThr = threshold;
// 定义新容量,新阈值为0
int newCap, newThr = 0;
// 当旧容量不为0的时候
// 这里要注意,当new 一个hashmap对象的时候,并不会马上初始化哈希表,但是会初始化阈值(会变成2的N次方),详见java.util.HashMap#HashMap(int, float)
// 在put的时候会进行resize,但第一次resize,也就是第一次put的时候,旧容量是0,
// 也就是说,当旧容量大于0的时候,也就不是第一次put的时候了
if (oldCap > 0) {
// 需要注意 MAXIMUM_CAPACITY = 1 << 30 等于 2的30次方,因为哈希表的长度要是2的N次方
// 当容量等于MAXIMUM_CAPACITY的时候说明容量已经不能再继续增加了
if (oldCap >= MAXIMUM_CAPACITY) {
// 这是将阈值设置为最大,就算到了这个阈值也不做什么了,不扩容,将原来的哈希表返回
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 这一步有点难理解
// 先将旧容量变成原来的两倍
// 1、因为当旧容量大于等于最大值的时候,阈值是要赋值为最大值的,这里的操作是要把阈值变为原来的两倍。所以旧容量变成原来的两倍之后,不能大于等于MAXIMUM_CAPACITY
// 当大于等于最大容量的时候,在下面进行赋值,不会说这次先不管阈值
// 2、为什么要扩容后的容量大于等于16呢?
// 如果一开始指定容量是2,那么阈值就是(int)(1*0.75),也就是1
// 在进行扩容之后,新容量变成4,这时如果对就阈值乘以两倍的话,新阈值就是2,那么负载因子就变成了0.5,而不是0.75了
// 也就是因为在容量小的时候,容量乘以负载因子存在误差,这个存在误差的值不能跟着容量的变大而变大,不然误差会越来越大
// 在容量小的时候,扩容后应该重新计算阈值
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
// 到这里,说明是第一次resize,因为容量为0,阈值在构造的时候指定了,将新容量设置为阈值
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
// 到这里,说明是第一次resize,并且构造的时候没有指定容量和阈值,将新容量和新阈值设置为默认的
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
// 到这里,说明以上的种种都没有成功对新阈值进行赋值
// 1、不是第一次resize,因为第一次resize的时候肯定会对新阈值进行赋值的
// 2、不是第一次resize,并且扩容后的容量小于16,也就是阈值需要有容量乘以负载因子算出来,而不是刻意将原来的的阈值翻倍
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;