什么情况下会进行resize()操作
1.HashMap初始化之后第一次put元素
2.HashMap中元素数量达到阈值
注意:
在对链表进行拆分的时候,会分为两个链表,因为数组扩容后长度是原来的二倍,元素在数组中下标的计算方式为:元素的hash值对数组的长度-1做与操作,数组长度发生变化,元素在数组中下标位置也可能发生变化
上源码
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) {
//如果当前数组大小>1 << 30 = 2的30次方,已达到最大容量,则不会扩容
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
//返回原数组
return oldTab;
}
//newCap = oldCap << 1将之前数组的大小左移一位(翻倍) 然后和2的30次方对比
//老数组要满足长度 >= 16
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
//将数组长度左移一位(翻倍) 新数组大小和阈值都是之前的一倍
newThr = oldThr << 1; // double threshold
}
//如果老数组长度不大于0且阈值>0
else if (oldThr > 0) // initial capacity was placed in threshold
//将老数组的阈值赋值给新数组
newCap = oldThr;
//老数组长度阈值都不大于0
else { // zero initial threshold signifies using defaults
//将新数组长度设置为16
newCap = DEFAULT_INITIAL_CAPACITY;
//将新数阈值(加载因子)设置为0.75
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_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 = newTab;
if (oldTab != null) {
//遍历老数组 j --> 老数组的下标
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
//判断老数组当前位置是否有元素
if ((e = oldTab[j]) != null) {
//移除当前元素
oldTab[j] = null;
//如果next为空说明当前位置只有一个元素
if (e.next == null)
//将该元素赋值到新数组中(位置:元素的哈希值对新数组长度-1做与操作)
newTab[e.hash & (newCap - 1)] = e;
//如果节点的next不为空:要么是链表要么是红黑树
//判断是否为红黑树
else if (e instanceof TreeNode)
//调用红黑树的扩容方法
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
//链表
else { // preserve order
//扩容时转移链表的逻辑(将老数组上的链表转移到新数组上)
//注意:老数组上链表的各个节点下标相同,转移到新数组后各个节点的下标可能会不同
//下标 = 节点的hash值对数组长度-1做与操作,新数组的长度与之前不同,所以下标会发生变化
//扩容之后数组的下标只有两种情况:1.与之前相等 2. 新下标 = 老下标+老数组长度
//lo: 低位链表 头节点 尾节点
//hi: 高位链表 头节点 尾节点
//lo和hi两个链表的作用就是保存原链表拆分成的两个链表
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
//do while循环对链表进行拆分操作
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) {
// 尾部节点next设置为null
loTail.next = null;
//数组下标不变的节点
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
//数组下标=原下标+原数组长度 的节点
newTab[j + oldCap] = hiHead;
}
}
}
}
}
//将新数组返回
return newTab;
}
|