/**
* 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.
*/
5条答案
按热度按时间x8diyxa71#
使用2的幂有效地屏蔽了散列代码的高位,因此质量差的散列函数在这种情况下可能表现得特别糟糕。
Java的
HashMap
通过不信任对象的hashCode()
实现并对其结果应用第二级散列来缓解这一问题:将补充哈希函数应用于给定的hashCode,以防止哈希函数质量差。这一点非常重要,因为HashMap使用2的幂长度哈希表,否则会遇到低位相同的hashCode冲突。
如果你有一个很好的散列函数,或者做一些类似于
HashMap
的事情,那么你是否使用质数等作为表的大小就没有关系了。另一方面,如果散列函数是未知的或者质量很差,那么使用素数可能是更安全的选择,但是这会使动态大小的表更难实现,因为突然之间你需要能够产生素数,而不仅仅是将大小乘以一个常数。
5kgi1eie2#
标准的HashMap实现有一个
hash
方法,它可以重新散列对象的散列代码以避免这个陷阱,hash()
方法前面的注解如下:bpsygsoo3#
要知道质数和2的幂哪个更好,唯一的方法就是对其进行基准测试。
很多年前,当我编写一个性能严重依赖于符号表查找的汇编程序时,我使用了一大块生成的标识符进行了测试。即使使用简单的Map,我也发现2的幂,正如预期的那样,比同样大小的质数个桶具有更少的均匀分布和更长的链。它仍然运行得更快,因为通过位屏蔽选择桶的速度更快。
我强烈怀疑java.util的开发者不会求助于额外的散列和2的幂,除非用质数个bucket作为基准,这是设计散列数据结构时很明显要做的事情。
出于这个原因,我确信rehash和2的幂大小比质数个bucket为典型的Java散列Map提供了更好的性能。
mpgws1up4#
从性能/计算时间的观点来看,可以仅利用位屏蔽来计算2的幂大小,这比否则将需要的整数模运算快。
2vuwiymt5#
如果你使用quadratic probing来解决冲突,你可能应该使用质数大小的哈希表。如果你有一个质数大小的表,二次探测将命中一半的条目,如果它不是质数,命中的条目会更少。所以即使你的哈希表还不到一半满,你也可能找不到合适的地方来存储条目。由于Java哈希Map不使用二次探测,所以没有必要使用质数作为大小。