【发布时间】:2019-03-12 13:35:03
【问题描述】:
HashMap 的文档中有这样一个短语:
如果初始容量大于最大条目数除以负载因子,则不会永远发生重新哈希操作。
注意文档是如何说 rehash,而不是 resize - 即使 rehash 只会在调整大小时发生;那是当桶的内部大小变成两倍大的时候。
当然HashMap 提供了这样一个构造函数,我们可以在其中定义这个初始容量。
构造一个具有指定初始容量和默认加载因子 (0.75) 的空 HashMap。
好的,看起来很简单:
// these are NOT chosen randomly...
List<String> list = List.of("DFHXR", "YSXFJ", "TUDDY",
"AXVUH", "RUTWZ", "DEDUC", "WFCVW", "ZETCU", "GCVUR");
int maxNumberOfEntries = list.size(); // 9
double loadFactor = 0.75;
int capacity = (int) (maxNumberOfEntries / loadFactor + 1); // 13
所以容量是13(内部是16 - 二次幂),这样我们保证文档部分不会重复。好的,让我们测试一下,但首先介绍一个方法,该方法将进入 HashMap 并查看值:
private static <K, V> void debugResize(Map<K, V> map, K key, V value) throws Throwable {
Field table = map.getClass().getDeclaredField("table");
table.setAccessible(true);
Object[] nodes = ((Object[]) table.get(map));
// first put
if (nodes == null) {
// not incrementing currentResizeCalls because
// of lazy init; or the first call to resize is NOT actually a "resize"
map.put(key, value);
return;
}
int previous = nodes.length;
map.put(key, value);
int current = ((Object[]) table.get(map)).length;
if (previous != current) {
++HashMapResize.currentResizeCalls;
System.out.println(nodes.length + " " + current);
}
}
现在让我们测试一下:
static int currentResizeCalls = 0;
public static void main(String[] args) throws Throwable {
List<String> list = List.of("DFHXR", "YSXFJ", "TUDDY",
"AXVUH", "RUTWZ", "DEDUC", "WFCVW", "ZETCU", "GCVUR");
int maxNumberOfEntries = list.size(); // 9
double loadFactor = 0.75;
int capacity = (int) (maxNumberOfEntries / loadFactor + 1);
Map<String, String> map = new HashMap<>(capacity);
list.forEach(x -> {
try {
HashMapResize.debugResize(map, x, x);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
});
System.out.println(HashMapResize.currentResizeCalls);
}
好吧,resize 被调用,因此条目被重新散列,而不是文档所说的。
如上所述,密钥不是随机选择的。这些设置是为了在桶转换为树时触发static final int TREEIFY_THRESHOLD = 8; 属性。不是真的,因为我们还需要点击MIN_TREEIFY_CAPACITY = 64 才能出现树;直到发生resize,或者桶的大小增加了一倍;因此会发生条目的重新散列。
我只能暗示为什么HashMap 文档在那句话中是错误的,因为在 java-8 之前,桶没有转换为树;因此该属性将成立,从 java-8 开始,这不再是真的。由于我不确定这一点,因此我不会将其添加为答案。
【问题讨论】:
-
有趣的发现 - [官方] 文档并不完美;这很可能是文档和实现之间的分歧..
-
@user2864740 你也可以投票here if you want。这是一个旧的,但仍然没有答案
-
呃...问题是什么?
-
@JornVernee ups,问题是,这真的是文档缺陷吗?斯图尔特给出的答案是“是”
-
无关:您给出了正确的提示。我了解到我的“小”助手项目......没有重新编译。我更改了另一个项目,设置为使用 java 7 语言级别,是的,它编译为 java7。所以不幸的是,我的问题没有意义留下来。所以我不得不删除它。我真的很讨厌被赞成的问题,因为我得到了太多的 0 或更少的问题,来自报复性的反对者……但你的问题在这里:很好!