HashMap的扩容函数
Node<K,V>[] resize()
阅读了HashMap的源码,对其扩容函数做了详细注释(基于JDK1.8)
/**
* HashMap扩容函数resize
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;//原来的hash桶数组
int oldCap = (oldTab == null) ? 0 : oldTab.length;//原来的哈希桶数组容量(数组长度)
int oldThr = threshold;//原来的扩容阈值
int newCap, newThr = 0;//新的容量和新的阈值
if (oldCap > 0) {//如果原来的容量大于0(也就是非初始化的时候)
if (oldCap >= MAXIMUM_CAPACITY) {//如果大于最大值,那么就没法再扩容了,只修改下扩容阈值为最大值,
threshold = Integer.MAX_VALUE;//
return oldTab;//然后返回
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&//如果没有到最大值,那么新的容量乘2
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold//新的扩容阈值乘2
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {//假如阈值为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 = newTab;
if (oldTab != null) {//假oldTab不为null
for (int j = 0; j < oldCap; ++j) {//遍历哈希桶数组
Node<K,V> e;
if ((e = oldTab[j]) != null) {//取出哈希桶数组的元素,如果这哈希桶中不为空
oldTab[j] = null;//将老哈希桶数组中的这个元素置为null.释放内存
if (e.next == null)//如果这个桶里面只有一个元素
newTab[e.hash & (newCap - 1)] = e;//直接放到新哈希桶数组对应的老哈希桶位置,e.hash & (newCap - 1)的值就是新的位置,因为桶大小为2的n次幂,所以这里等于做了取模运算。
else if (e instanceof TreeNode)//检查是不是红黑树,如果是红黑树,那就插到红黑树里面
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order//把老哈希桶中冲突的元素的头指针和尾指针找出来
Node<K,V> loHead = null, loTail = null;//需要移动到 +oldCap 位置的节点,也就是不移动的节点
Node<K,V> hiHead = null, hiTail = null;//需要移动到 现在位置+oldCap 位置的节点
Node<K,V> next;
//判断是不是应该移动,这里不需要重新计算hash值来映射到新的数组位置
//因为扩容了2倍,所以,取模运算后(这里实际没有取模,而是用位运算替代了),原来哈希桶数组位置不是在原位置就是向左移动一位
//只需要比较哈希值原来位的上面一位是不是1就知道了,如果是1,那么位置向左移动一位(现在位置+oldCap)否则不用移动
//比较的方法是新的哈希桶新的容量大小和哈希值按位与:e.hash & oldCap
//然后将哈希桶数组中当前节点分为两类,一类是需要移动的,一类是不需要移动的(相对位置)
//最后,把分好的两个链表放到新table的原来的位置和新table的 现在位置+oldCap 位置
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;//添加到新哈希桶的 j+oldCap位置
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}