作者:泉州多棱汽车销售服务有限公司 | 来源:互联网 | 2023-06-07 17:26
1.什么是resize:resize就是重新计算容量;当我们不断的向HashMap对象里不停的添加元素时,HashMap对象内部的数组就会出现无法装载更多的元素,这是对象就需要扩大数组的长
1.什么是resize:
resize就是重新计算容量;当我们不断的向HashMap对象里不停的添加元素时,HashMap对象内部的数组就会出现无法装载更多的元素,这是对象就需要扩大数组的长度,以便能装入更多的元素;当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组;就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。
2.什么时候需要resize():
当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值—即当前数组的长度乘以加载因子的值的时候,就要自动扩容。
扩容:(源码 661-662)
计算阀值:
1.第一次创建Hash表时:
2.对HashMap进行扩容时:
3.源码分析:
final Node[] resize() {
Node[] 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) = 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);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node[] newTab = (Node[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j 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)e).split(this, newTab, j, oldCap);
else {
Node loHead = null, loTail = null;
Node hiHead = null, hiTail = null;
Node 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;
}
}
}
}
}
return newTab;
}
以链表形式存在的节点:
存在两个数他们的 Hash 值分别为:5,21 二进制形式分别为(0101,10101)。
若没有进行扩容时容量为 16,进行扩容之后的容量为 32
坐标点的计算(计算规则 :e.hash & (newCap - 1)):
没有进行扩容时:
可以看到两个Hash值所计算的坐标是相同的。
进行扩容之后:
可以看出经过扩容之后,两次计算的坐标出现了不同,但是第二个坐标点增加了 oldCap 个长度。
再看看 e.hash & oldCap 所计算出的结果:
可以看到当 e.hash & oldCap == 0 是,原来的坐标没有发生变化,e.hash & oldCap != 0 在原来坐标的前提下增加 oldCap 。
两条链表的连接过程:
在向表中连接的时候最后一个节点的下一个节点做空。
总结:
- 在对 HashMap 进行扩容时,阀值会变为原来的两倍;
- 在对HashMap进行扩容的时候,HashMap的容量会变为原来的两倍;
- 扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。
- 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。