许多书籍和教程都说哈希表的大小必须是在所有存储桶中均匀分配密钥的主要原因。但是Java的HashMap
总是使用2的幂。不应该使用素数吗?什么更好,一个“素数”或“两个幂”作为哈希表大小?
答案 0 :(得分:22)
使用2的幂有效地掩盖哈希码的顶部位。因此,在这种情况下,质量差的哈希函数可能表现得特别糟糕。
Java HashMap
通过不信任对象的hashCode()
实现和applying a second level of hashing to its result来缓解这种情况:
将补充哈希函数应用于给定的hashCode,以防御质量差的哈希函数。这很关键,因为HashMap使用两个幂的长度哈希表,否则会遇到低位不同的hashCodes的冲突。
如果你有一个好的哈希函数,或者做类似于HashMap
的事情,那么你是否使用素数等作为表格大小并不重要。
另一方面,如果散列函数质量未知或质量差,那么使用素数将是一个更安全的赌注。但是,它会使动态大小的表格变得难以实现,因为突然之间你需要能够产生素数而不是仅仅将大小乘以常数因子。
答案 1 :(得分:3)
标准的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.
*/
答案 2 :(得分:3)
要知道素数和二次幂之间哪个更好的唯一方法就是对它进行基准测试。
许多年前,当编写一个性能强烈依赖于符号talbe查找的汇编程序时,我使用一大块生成的标识符对其进行了测试。即使有一个天真的映射,我发现正如预期的那样,二次幂的分布比一个类似大小的素数桶的分布更均匀,链更长。由于通过位屏蔽选择桶的速度,它仍然运行得更快。
我强烈怀疑java.util开发人员在没有使用大量数据桶进行基准测试的情况下不会使用额外散列和2次幂。在设计散列数据结构时,这是非常明显的事情。
出于这个原因,我确信rehash和power-of-two大小为典型的Java哈希映射提供了比质数桶更好的性能。
答案 3 :(得分:0)
从性能/计算时间的角度来看,可以仅使用比特掩码来计算两个幂的大小,这比整数除法更快,否则将是必需的。
答案 4 :(得分:0)
如果使用quadratic probing进行冲突解决,则可能应该使用素数大小的哈希表。如果你有一个素数大小的表,二次探测将击中一半的条目,如果它不是素数则更少。因此,即使您的哈希表小于半满,您也可能找不到合适的存储位置。由于Java哈希映射不使用二次探测,因此不需要使用素数作为大小。