HashMap 源码resize () 扩容方法
Posted HaSaKing_721
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap 源码resize () 扩容方法相关的知识,希望对你有一定的参考价值。
前言
扩容的逻辑大概分为两步:
- 1)计算新的容量和扩容阀值,并创建新的 table 数组;
- 2)将老的 table 复制到新的 table 数组中
源码
final Node<K,V>[] resize() {
//将之前的数组进行保存
Node<K,V>[] oldTab = table;
//旧数组的长度
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//旧数组的扩容阈值(数组容量 * 加载因子(0.75))
int oldThr = threshold;
//初始化新数组的长度和阈值
int newCap, newThr = 0;
/** 第一步:计算新的容量和扩容阀值,并创建新的 table 数组 **/
// 旧数组的长度 大于 0 ,说明 table 非空,进行两倍扩容
if (oldCap > 0) {
//超过最大容量,则直接设置 threshold 阀值为 Integer.MAX_VALUE ,不再允许扩容
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 新数组的长度 = 旧数组的长度<< 1 ,目的是两倍扩容
// 如果 旧数组的长度 >= DEFAULT_INITIAL_CAPACITY 满足,说明当前容量大于默认值(16),则 2 倍阀值。
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // 可以理解为 newCap * loadFactor 位运算更快
}
// 旧数组的扩容阈值 大于 0 ,则使用 旧数组的扩容阈值 作为新的容量
else if (oldThr > 0)
newCap = oldThr;
else {// 旧数组的扩容阈值 等于或下雨 0 ,则使用 DEFAULT_INITIAL_CAPACITY 作为新的容量,使用 DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY 作为新的容量
newCap = DEFAULT_INITIAL_CAPACITY;//长度为16
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//阈值为16*0.75
}
//如果上述的逻辑,未计算新的阀值,则使用 newCap * loadFactor 作为新的阀值
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
/** 第二步:将老的 table 复制到新的 table 数组中 **/
// 将 新数组阈值 赋值给 threshold 属性
threshold = newThr;
// 创建新的 Node 数组,赋值给 table 属性
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
// 如果老的 table 数组非空,则需要进行一波搬运
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
// 获得老的 table 数组第 j 位置的 Node 节点 e
Node<K,V> e;
if ((e = oldTab[j]) != null) {
// 置空老的 table 数组第 j 位置
oldTab[j] = null;
// 如果 e 节点只有一个元素,直接赋值给新的 table 即可
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
//如果 e 节点是红黑树节点,则通过红黑树分裂处理
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
//如果 e 节点是链表
else { // preserve order
// HashMap 是成倍扩容,这样原来位置的链表的节点们,会被分散到新的 table 的两个位置中去
// 通过 e.hash & oldCap 计算,根据结果分到高位、和低位的位置中。
// 1. 如果结果为 0 时,则放置到低位
// 2. 如果结果非 1 时,则放置到高位
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
// 这里 do while 的原因是,e 已经非空,所以减少一次判断。细节~
do {
// next 指向下一个节点
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);
// 设置低位到新的 newTab 的 j 位置上
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
// 设置高位到新的 newTab 的 j + oldCap 位置上
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
流程图
//TODO
以上是关于HashMap 源码resize () 扩容方法的主要内容,如果未能解决你的问题,请参考以下文章