1

而嵩山版则可以看到:

同时我们也可在嵩山版的版本历史中看到对以上变化的描述:

-
https://developer.aliyun.com/topic/java20
-
扩容次数修改为resize次数。主要的问题是每个人对「扩容」这个定义存在分歧。 -
扩容的主要分歧是在:没有给HashMap进行初始化的时候,第一次put的时候才会分配空间,也会调用resize。那这操作个算不算扩容?即初始化容量这个操作算不算扩容? -
所以扩容次数修改为resize次数。
2
-
“如果你new了一个HashMap它并不会给你分配空间。同时文档中也写了由于没有设置容量初始大小。 -
“除了这个条件其实还有一个额外的条件,就是这是在JDK1.8之后HashMap才会有的操作。

存在四个构造方法
//参数:初始容量,负载因子
public HashMap(int initialCapacity, float loadFactor) { }
//参数:初始容量。调用上一个构造函数,默认的负载因子(0.75)
public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); }
//参数:无参构造器。创建的HashMap具有默认的负载因子(0.75)
public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
//参数:使用与指定Map相同的映射构造一个新的HashMap。创建的HashMap具有默认的负载因子(0.75)和足够在指定Map中保存映射的初始容量。
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
也就是说当我们使用一个无参构造器创建HashMap的时候
Map<String,String> map = new HashMap<>();
而当我们对这个HashMap进行put()操作的时候
Map<String,String> map = new HashMap<>();
map.put("first","firstValue");
我们看一下源码,进行了什么操作?

public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/**
* Implements Map.put and related methods.
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
3
-
HashMap没有初始化容量时默认负载因子为0.75,在第一次put()时会调用resize()进行初始化,容量为16。(JDK1.8) -
HashMap中的阈值threshold为capacity * load factor就容量*负载因子。例如容量为16,那么阈值threshold就为16*0.75=12。 -
当HashMap进行put()的时候,元素个数大于等于当前阈值的时候size>=threshold,会进行resize()操作。容量和阈值都会乘以2。例如初始容量为16,那么当我们put第12个元素的时候,就会进行resize()操作。
基于以上已知的结论,那么我们就能很容易的知道,在JDK1.8中没有给HashMap初始化容量时,存储1024个元素调用resize()的次数和时机了。(默认阈值为0.75)

4
-
前者的人认为扩容次数为8次。 -
后者的人认为扩容次数为7次。
【福利】2023 高薪课程,全面来袭(视频+笔记+源码)
往期推荐

