[英]Hashmap loadfactor - based on number of buckets occupied or number of entries in all buckets?
我试图了解 hashmap 的重新散列是在超过占用的桶数或所有桶中的条目总数时发生的。 意味着,我们知道如果 16 个桶中的 12 个(每个桶中有一个条目)已满(考虑默认负载因子和初始容量),那么我们知道在下一个条目中 hashmap 将被重新散列。 但是,如果假设只有 3 个存储桶被每个 4 个条目占用(总共 12 个条目,但 16 个存储桶中只有 3 个在使用中),那这种情况呢?
所以我试图通过制作最糟糕的 hash function 来复制这一点,这会将所有条目放在一个桶中。
这是我的代码。
class X {
public Integer value;
public X(Integer value) {
super();
this.value = value;
}
@Override
public int hashCode() {
return 1;
}
@Override
public boolean equals(Object obj) {
X a = (X) obj;
if(this.value.equals(a.value)) {
return true;
}
return false;
}
}
现在我开始在 hashmap 中输入值。
HashMap<X, Integer> map = new HashMap<>();
map.put(new X(1), 1);
map.put(new X(2), 2);
map.put(new X(3), 3);
map.put(new X(4), 4);
map.put(new X(5), 5);
map.put(new X(6), 6);
map.put(new X(7), 7);
map.put(new X(8), 8);
map.put(new X(9), 9);
map.put(new X(10), 10);
map.put(new X(11), 11);
map.put(new X(12), 12);
map.put(new X(13), 13);
System.out.println(map.size());
所有节点都按预期进入了单个存储桶,但我注意到在第 9 次进入时,hashmap 重新哈希并使其容量增加了一倍。 现在在第 10 次入境时,它的容量再次翻了一番。
谁能解释这是怎么回事?
提前致谢。
回答评论而不是问题本身,因为您的评论与您实际想知道的内容更相关。
最佳和最相关的答案是对源代码本身进行where this rehashing on bucket size is explained further
。 您在第9-th
条目上观察到的内容是预期的并且在代码的这一部分中发生:
for (int binCount = 0; ; ++binCount) {
// some irrelevant lines skipped
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
其中TREEIFY_THRESHOLD = 8
, binCount
是箱数。
那个treeifyBin
方法名称有点误导,因为它可能会重新调整大小,而不是树形容器,这是该方法代码的相关部分:
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
请注意,它实际上将resize
(阅读它的两倍大小),而不是做一个Tree
,直到MIN_TREEIFY_CAPACITY
达到(64)。
在HashMap中,如果条目的哈希码相同,则条目将出现在同一个桶中。 如果将唯一的Integer对象放在hashMap中,则它们的hashcode肯定会发生变化,因为它们是不同的对象。
但在您的情况下,所有对象都具有相同的哈希码。 这意味着您指定的所有条目都将在一个存储桶中。 这些桶中的每一个都遵循特定的数据结构(linkedList / tree)。 这里的容量根据特定的数据结构和hashmap而变化。
我已经在评论中提到了JB Nizet的代码( https://gist.github.com/jnizet/34ca08ba0314c8e857ea9a161c175f13/revisions ),循环限制为64和128(添加了64和128个元素):
将容量增加到64后,HashMap正常工作。
总之,bucket使用链表到一定长度(8个元素)。 之后,它使用树数据结构(容量有波动)。 原因是访问树结构(O(log(n)))比链表(O(n))更快。
此图显示了JAVA 8 HashMap的内部数组,其中包含两个树(在桶0处)和链接列表(在桶1,2和3处)。 Bucket 0是一棵树,因为它有超过8个节点( readmore )。
在这方面,有关Hashmap的文档以及有关hashmap中存储桶的讨论会很有帮助。
阅读hashmap的源代码,
/** * The smallest table capacity for which bins may be treeified. * (Otherwise the table is resized if too many nodes in a bin.) * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts * between resizing and treeification thresholds. */ static final int MIN_TREEIFY_CAPACITY = 64;
你会看到的
调整大小和树化是两个可以进行地图重组的操作,基于不同场景的上述决策也是一种权衡。
根据 hashmap 中的存储桶数组 (b) 占用数和条目数 (n) 计算负载因子的简单数学公式为 n/b。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.