《码出高效》中有这么一句话: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()
方法。
1
daimazha 2020-03-11 18:22:02 +08:00
这里指的是 table
|
2
az467 2020-03-11 18:26:02 +08:00
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } |
3
Lonely 2020-03-11 18:26:43 +08:00 via iPhone
那就话应该是说内部的 node 数组是在第一次 put 的时候初始化。这种问题,你点进去源码看两眼不就很清楚了。
|
4
hhhsuan 2020-03-11 18:29:00 +08:00 via Android
容量大小是 16,但空间未必已经分配了,这是两码事
|
5
asche910 2020-03-11 19:23:04 +08:00
源码 put 的时候,如果未初始化,会首先调用一次 resize 方法
|
6
coer 2020-03-12 01:48:55 +08:00 via Android
打断点看构造函数,这样怎么看的出来啊
|
7
sumulige 2020-03-12 03:58:20 +08:00 via iPhone
@hhhsuan new 的过程不是初始化 初始化不就是分配了内存空间 还是起初哪个 16 只是个值并没有完成实例化
|
8
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 |
9
afpro 2020-03-12 09:59:38 +08:00
emmm 我尝试用 ``` 放代码 但是没成功 这个怎么样才能正确的贴代码?
|
10
MatthewHan OP @afpro #8 我的代码里不是没 put 吗
|
11
afpro 2020-03-12 10:13:19 +08:00
@MatthewHan 你是没 put 啊 所以只初始化了两个参数 没有 new table 你的疑惑在哪呢??
|
12
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 可好。。 |
13
MatthewHan OP @afpro #12 所以我不是问这句话怎么理解比较好🐴,不是说我没看源码。。。
|
15
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 ====源码==== |
16
Aresxue 2020-03-12 16:40:19 +08:00
默认在 class 中,一直都是 16(常量),你 new 的时候只是建立了引用,put 的时候才会真正分配内存空间
|
17
ShellMings 2020-03-13 08:36:19 +08:00 via iPhone
码出高效 看看就好 呵呵 😄
|