HashMap扩容代码主要可以分为entry数组扩容以及历史元素重新rehsh转移到新扩容的entry数组中
第一步entry数组扩容
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) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1;
}
else if (oldThr > 0)
newCap = oldThr;
else {
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
threshold = newThr;
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
}
第二步,把历史元素重新rehash转移到新扩容的entry数组中
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
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 {
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) {
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;
newTab[j + oldCap] = hiHead;
}
}
}
}
要点总结
- 为什么链表形状的entry进行拆分之后,一部分是放入到新entry数组的j位置,一部分放入的是新entry数组的 j+ oldCap位置(为什么扩容都是原来的两倍)
- entry链表拆分维度是根据(e.hash & oldCap) == 0来拆分的,e.hash & oldCap = 1则放入新entry数组的j + oldCap位置;
- 由于oldCap的大小都是2的幂次方以及新entry数组的大小是oldCap*2;所以二进制表示中如果oldCap是10000(16)那么新entry数组的大小newCap就是100000(32)
- 所以e.hash & oldCap = 1 <=>(e.hash & 10000)=1 满足的话,说明e.hash的二进制表示中第5位必定是1
- Map获取元素的get方法中,从entry数组中定位下标时用的是tab[(n - 1) & hash],n代表的是entry数组大小;
- 在未扩容前n=16,(n - 1) & hash <=> 1111 & e.hash;扩容后n=32,(n - 1) & hash <=> 11111 & e.hash;在上述推导得出如果e.hash & oldCap = 1,那么说明在未扩容旧数组大小的二进制表示中为1的那个位置,e.hash也必定为1。所以11111 & e.hash <=> oldCap + (1111 & e.hash),所以e.hash & oldCap = 1则放入新entry数组的 j+ oldCap位置!