HashMap 无显式赋值容量大小初始化,默认的 16 是在 new 的过程中,还是在第一次 put 的时候?

2020-03-11 18:08:32 +08:00
 MatthewHan

《码出高效》中有这么一句话:HashMap 容量并不会在 new 的过程中分配,而是在第一次 put 的时候完成创建。

文中的源码环境是 JDK11。

我在本地环境 JDK8 的代码里这样写:

// 未显式初始化容量大小
Map<String, String> map = new HashMap<>();
Class<?> mapClazz = map.getClass();
Method capacity = mapClazz.getDeclaredMethod("capacity");
capacity.setAccessible(true);
System.out.println("不显式的初始化,容量大小为:" + capacity.invoke(map));

输出的结果为:不显式的初始化,容量大小为:16。

我以为会和ArrayList一样,未显式初始化,容量大小是 0,只有调用一次 add 方法后,才会扩容成默认值的容量大小。

那《码出高效》这句话该怎么解释好呢?是 JDK 版本的问题吗?反射的是capacity()方法。

2807 次点击
所在节点    Java
17 条回复
daimazha
2020-03-11 18:22:02 +08:00
这里指的是 table
az467
2020-03-11 18:26:02 +08:00
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
Lonely
2020-03-11 18:26:43 +08:00
那就话应该是说内部的 node 数组是在第一次 put 的时候初始化。这种问题,你点进去源码看两眼不就很清楚了。
hhhsuan
2020-03-11 18:29:00 +08:00
容量大小是 16,但空间未必已经分配了,这是两码事
asche910
2020-03-11 19:23:04 +08:00
源码 put 的时候,如果未初始化,会首先调用一次 resize 方法
coer
2020-03-12 01:48:55 +08:00
打断点看构造函数,这样怎么看的出来啊
sumulige
2020-03-12 03:58:20 +08:00
@hhhsuan new 的过程不是初始化 初始化不就是分配了内存空间 还是起初哪个 16 只是个值并没有完成实例化
afpro
2020-03-12 09:59:06 +08:00
```
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
```

显然只初始化了一个 float 和一个 int

```
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}

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;
...
}
```

put 的时候如果 (tab = table) == null 会去 resize

```
final Node<K,V>[] resize() {
...
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
...

```

在 resize 里面 new 了新的 table
afpro
2020-03-12 09:59:38 +08:00
emmm 我尝试用 ``` 放代码 但是没成功 这个怎么样才能正确的贴代码?
MatthewHan
2020-03-12 10:08:54 +08:00
@afpro #8 我的代码里不是没 put 吗
afpro
2020-03-12 10:13:19 +08:00
@MatthewHan 你是没 put 啊 所以只初始化了两个参数 没有 new table 你的疑惑在哪呢??
afpro
2020-03-12 10:14:37 +08:00
@MatthewHan 你疑惑的是 capacity 这个方法??

final int capacity() {
return (table != null) ? table.length :
(threshold > 0) ? threshold :
DEFAULT_INITIAL_CAPACITY;
}

这不是显然 table 为 null 的时候返回了 threshold 或者 DEFAULT_INITIAL_CAPACITY

有发帖和跟帖的时间瞄一眼 code 可好。。
MatthewHan
2020-03-12 10:21:50 +08:00
@afpro #12 所以我不是问这句话怎么理解比较好🐴,不是说我没看源码。。。
hhhsuan
2020-03-12 10:35:03 +08:00
@sumulige #7 没有分配那 16 个空位,直到第一次存数据的的时候才会。
Angzk3348
2020-03-12 12:18:14 +08:00
先来看看
new HashMap<>();
只是初始化了 loadFactor 的值.
====源码====
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
====源码====


再来看看
capacity()
因为调用的是 无参构造器.
table 肯定是 null 第一个三目 是 false.
threshold 没赋值 第二个三目 也是 false.
====源码====
final int capacity() {
return (table != null) ? table.length : (threshold > 0) ? threshold :DEFAULT_INITIAL_CAPACITY;
}
====源码====

那么 再来看看
DEFAULT_INITIAL_CAPACITY
====源码====
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
====源码====
Aresxue
2020-03-12 16:40:19 +08:00
默认在 class 中,一直都是 16(常量),你 new 的时候只是建立了引用,put 的时候才会真正分配内存空间
ShellMings
2020-03-13 08:36:19 +08:00
码出高效 看看就好 呵呵 😄

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/651940

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX