【发布时间】:2013-03-04 11:01:07
【问题描述】:
许多书籍和教程都说哈希表的大小必须是素数,才能在所有桶中均匀分布密钥。但是Java 的HashMap 总是使用2 的幂的大小。不应该使用素数吗?哈希表大小是“质数”还是“2 的幂”哪个更好?
【问题讨论】:
-
我怀疑他们是否真的这么说,如果他们这样做了,那就错了。这只是一种方法。
标签: java hash hashmap hashtable hashcode
许多书籍和教程都说哈希表的大小必须是素数,才能在所有桶中均匀分布密钥。但是Java 的HashMap 总是使用2 的幂的大小。不应该使用素数吗?哈希表大小是“质数”还是“2 的幂”哪个更好?
【问题讨论】:
标签: java hash hashmap hashtable hashcode
使用 2 的幂可以有效地屏蔽哈希码的最高位。因此,在这种情况下,质量差的哈希函数可能会表现得特别差。
Java 的 HashMap 通过不信任对象的 hashCode() 实现和 applying a second level of hashing to its result 来缓解这种情况:
对给定的 hashCode 应用补充散列函数,以防止质量差的散列函数。这一点很关键,因为 HashMap 使用长度为二的幂的哈希表,否则会遇到低位没有差异的 hashCode 的冲突。
如果你有一个好的散列函数,或者做一些类似于HashMap 所做的事情,那么你是否使用素数等作为表大小都没有关系。
另一方面,如果散列函数未知或质量差,那么使用质数将是更安全的选择。但是,它会使动态大小的表格难以实现,因为突然之间您需要能够生成素数,而不是仅仅将大小乘以一个常数因子。
【讨论】:
标准的 HashMap 实现有一个 hash 方法,它可以重新散列对象的哈希码以避免这种陷阱。 the hash() method 之前的评论内容为:
/**
* Retrieve object hash code and applies a supplemental hash function to the
* result hash, which defends against poor quality hash functions. This is
* critical because HashMap uses power-of-two length hash tables, that
* otherwise encounter collisions for hashCodes that do not differ
* in lower bits. Note: Null keys always map to hash 0, thus index 0.
*/
【讨论】:
了解素数和二次幂之间哪个更好的唯一方法是对其进行基准测试。
许多年前,在编写一个性能强烈依赖于符号表查找的汇编程序时,我使用大量生成的标识符对其进行了测试。即使使用简单的映射,我发现与预期的一样,与类似大小的素数桶相比,二次幂的分布更不均匀,链更长。由于位掩码的桶选择速度,它仍然运行得更快。
我强烈怀疑 java.util 开发人员不会使用额外的散列和二的幂,而不是针对使用质数桶进行基准测试。在设计散列数据结构时,这是一件非常明显的事情。
出于这个原因,我确信 rehash 和二次幂大小为典型的 Java 哈希映射提供了比质数桶更好的性能。
【讨论】:
从性能/计算时间的角度来看,可以仅使用位掩码来计算二次方大小,这比整数除法要快,否则需要整数除法。
【讨论】:
如果您使用quadratic probing 解决冲突,您可能应该使用素数大小的哈希表。如果您有一个素数大小的表,二次探测将命中一半的条目,如果不是素数则更少。因此,即使您的哈希表未满一半,您也可能找不到合适的位置来存储您的条目。由于 Java 哈希映射不使用二次探测,因此无需使用素数作为大小。
【讨论】: