这篇文章是针对 前两篇
hashmap 实现原理总结
hashmap 源码解析 new HashMap()
的补充,主要是讲解hashmap 是怎么进行扩容的。
先大概说下总结,后面在进行详细描述
- 如果使用的无参的构造函数申明的hashmap 在第一次put 数据的时候回进行初始化扩容,初始化map 大小是16, 扩容阈值是 12
- 如果使用了带参数的构造函数申明的hashmap, 在第一次put 数据的时候也会进行初始化扩容(new的时候 并没有生成一个完整的map) 初始化 map 大小是传入参考最接近2的整数次幂的数, 扩容阈值是 map 大小 *0.75
- 如果不是首次扩容 且没有超过 最大限制, 直接 扩容一倍
什么时候会进行扩容
通过查看hashmap putVal() 的源码 发现有两个地方会触发扩容机制
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
就是table 为空,还没有插入元素的时候, 这时候进行的是初始化扩容
if (++size > threshold)
resize();
map 里面元素的个数大于 扩容阈值(threshold) 的时候,会触发扩容机制
如何进行扩容
我们查看 resize() 函数,通过阅读源码来梳理 hashmap是如何进行扩容的
final Node<K,V>[] resize() {
// 如果是第一次put 数据 , tables 是 null
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
// 扩容阈值
// 1. 如果是 new hashmap() 第一次put的时候 threshold为0
// 2. 如果是 new hashmap(n) 第一次put 的时候 threshold 为 最接近n 的2的整数次幂的数 (https://blog.youkuaiyun.com/sessionsong/article/details/98211616)
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) { // 老容器不为空,所有是第二次或者是第n 次扩容
if (oldCap >= MAXIMUM_CAPACITY) { // 操作最大限制
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && // 没有超过 扩容一倍
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
// 如果oldThr >0 也就是threshold >0 也就是 new HashMap(int initialCapacity)/ HashMap(int initialCapacity, float loadFactor) 形式申明的map 对象
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
// new hashmap()形式申明的对象
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) { // 重点: newThr 只有 oldCap 为空 且 oldThr为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;
// 下面是 扩容后 元素复制逻辑 可以暂时不研究