当您知道HashSet中可能的最大元素数量时,应使用什么负载因子?

7
当我确切知道HashSet中元素的最大可能数量时,应该使用什么负载因子?我听说默认的0.75负载因子被推荐使用,因为它在速度和空间之间提供了良好的性能折衷。这是正确的吗?但是一个更大的HashSet创建需要更多时间和更多的空间。
我只是使用HashSet来从整数列表中删除重复的整数。

除非你打算有大量的集合,否则不必担心。除非你的集合中有成千上万的条目,否则你可能看不到任何区别。 - MeBigFatGuy
他指的“10万”其实是“百万”。 - corsiKa
4个回答

5

我曾经花了一些时间来玩转负载因子,令人惊讶的是,在实际应用中,这个设置所起到的作用非常小。即使将其设置为像2.0这样的高值,也不会明显减慢速度,也不会节省多少内存。就当它不存在吧。Josh经常后悔把它作为一个选项公开出来。


你有最后一句话所提到的那篇文章链接吗? - Pacerier
2
@Pacerier:我强烈怀疑最后一句话是从面对面的交谈中得来的,因为凯文和乔希经常有所交流。 - Daniel Martin

2
针对您提出的问题,除了使用 HashSet 外,您还可以考虑使用 BitSet
根据您整数的范围和稀疏程度,使用 BitSet 可能会获得更好的性能和空间特性。

1

这很大程度上取决于你的整数。负载因子的作用是“平衡”哈希函数:对于“完美”的哈希函数,负载因子可以达到1.0。然而,如果所涉及的整数值呈现出任何形式的规律性,可能会导致比平均水平更多的哈希冲突,这会降低映射的效率。因此,较低的负载因子可能有助于更好地分散值(在更大的范围内),从而减少哈希冲突。

我不会过多担心使用较低的负载因子所需的创建时间和额外空间 - 我认为你几乎不会注意到差异(除非你使用硬件受限的平台,或者在你的映射中有数百万个整数 - 那么大小差异可能变得明显,每100万个值大约增加几兆字节)。


它们是完全随机的整数。实际上,它们是我应用程序中用户ID的列表。 - Rajat Gupta
@Marcos,我认为很多人会对你使用计算机程序生成“完全随机”值的方法感兴趣;-)那么用户ID是如何生成的呢? - Péter Török
整数的hashCode方法返回整数值本身。这是一个完美的哈希:只有一个整数具有给定的哈希值。因此,您的负载因子可以为1.0。 - JB Nizet
@JB Nizet,对于任何预定义的哈希映射大小,我都可以向您展示一组整数,总是导致哈希冲突。也就是说,每个值最终都会落入同一个桶中(有效地将映射降级为链接列表)。 - Péter Török
@JB Nizet 鉴于大多数哈希映射实现都会重新散列,这可能会有问题(除非您关闭了重新散列)。 - corsiKa

0

如果您确切地知道应该有多少个,您应该将负载因子设置为1,并确保您的哈希函数映射为1:1。您可能需要扩展容器以避免重新散列哈希。

请注意,这种“精确”的事情往往会随着时间的推移而改变,因此最好使用普通容器。 :)

编辑:我的回答是在我不知道它是整数之前。

是的,最好的选择就是保持原样。您永远不会注意到差异。

/**
 * Remove duplicates from a list. 
 * @note This will ALTER the list. 
 * @note This is not thread safe.
 * @param the list (potentially with duplicates)
 */
void removeDuplicates(List<Integer> list) {
    Set<Integer> noDupe = new HashSet<Integer>(list.size()); // will end up resizing once, oh well
    for(Integer i : list) noDupe.add(i);
    list.clear();
    list.addAll(noDupe);
}

Google Guava库避免了Maps和Sets方法中的一次调整大小,使用newHashMapWithExpectedSize()和newHashSetWithExpectedSize()。它计算出一个足够大的初始容量,以避免重新调整大小。在某些性能场景下,您可以注意到重新调整大小和设置负载因子为1的差异(如其他答案中提到的降级哈希)。始终进行测试、调整和再次测试。 - Carl Pritchett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接