关于java:为什么在hashCode中使用素数?

Why use a prime number in hashCode?

我只是想知道为什么素数被用于类的hashCode()方法?例如,当使用Eclipse生成我的hashCode()方法时,总是使用质数31

1
2
3
4
public int hashCode() {
     final int prime = 31;
     //...
}

参考文献:

下面是关于哈希代码的一个很好的初级读物,以及我发现的关于哈希如何工作的文章(C,但是概念是可传递的):Eric Lippert的GetHashCode()指南和规则


选择质数是为了在哈希桶中最佳地分布数据。如果输入的分布是随机的,并且分布均匀,那么哈希代码/模数的选择就不重要了。只有当输入有特定的模式时,它才有影响。

在处理内存位置时,通常是这样。例如,所有32位整数都与可被4整除的地址对齐。请查看下表,以可视化使用素数与非素数模的效果:

1
2
3
4
5
6
7
8
9
Input       Modulo 8    Modulo 7
0           0           0
4           4           4
8           0           1
12          4           5
16          0           2
20          4           6
24          0           3
28          4           0

注意,当使用素数模与非素数模时,几乎是完美的分布。

然而,尽管上面的例子很大程度上是人为的,但一般的原则是,当处理输入模式时,使用质数模将产生最佳分布。


因为您希望您要乘以的数字和您要插入的存储桶的数量有正交的主因子分解。

假设有8个桶要插入。如果您用来乘以的数字是8的某个倍数,那么插入的bucket将只由最低有效的条目(一个根本不乘以的条目)确定。类似的条目将发生冲突。不适合哈希函数。

31是一个足够大的素数,桶的数量不可能被它整除(事实上,现代Java HASMAP实现将桶的数量保持为2的幂)。


对于它的价值,有效的Java第二版手放弃数学问题,只是说选择31的原因是:

  • 因为它是一个奇数素数,使用素数是"传统"的
  • 它也是小于二次幂的一个,允许按位优化

以下是第9项的完整报价:当您覆盖equals时,始终覆盖hashCode

The value 31 was chosen because it's an odd prime. If it were even and multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting. The advantage of using a prime is less clear, but it is traditional.

A nice property of 31 is that the multiplication can be replaced by a shift (§15.19) and subtraction for better performance:

1
 31 * i == (i << 5) - i

Modern VMs do this sort of optimization automatically.

While the recipe in this item yields reasonably good hash functions, it does not yield state-of-the-art hash functions, nor do Java platform libraries provide such hash functions as of release 1.6. Writing such hash functions is a research topic, best left to mathematicians and theoretical computer scientists.

Perhaps a later release of the platform will provide state-of-the-art hash functions for its classes and utility methods to allow average programmers to construct such hash functions. In the meantime, the techniques described in this item should be adequate for most applications.

简单地说,使用带有许多除数的乘法器将导致更多的哈希冲突。因为为了有效地散列,我们希望最小化冲突的数量,所以我们尝试使用一个除数更少的乘法器。根据定义,素数正好有两个不同的正除数。

相关问题

  • 来自一个字段的Java哈希代码——配方,再加上使用Apache Con on LangeBu建器的例子
  • 将对象的hashcode定义为所有类变量hashcode的和、乘或其他形式是否不正确?
  • 绝对初学者的位移动指南?


我听说选择31是为了让编译器可以将乘法优化为左移位5位,然后减去该值。


首先计算散列值模2^32(int的大小),所以需要一些相对质数为2^32的值(相对质数意味着没有常见的除数)。任何奇数都可以。

然后,对于给定的哈希表,索引通常是从哈希值模到哈希表的大小来计算的,所以您需要一些与哈希表的大小相对应的素数。由于这个原因,哈希表的大小通常被选为质数。在Java的情况下,Sun实现确保大小总是两个幂,因此奇数也足够了。还有一些额外的散列键按摩来进一步限制冲突。

如果哈希表和乘数有一个共同的因素n,则可能会产生不良影响,即在某些情况下,哈希表中只使用1/N项。


这是一个引文,离来源稍微近一点。

归根结底是:

  • 31是质数,可以减少碰撞。
  • 31生产一个良好的分配,与
  • 合理的速度权衡


为什么the reason is used to minimize原号码是collisions when the exhibits some是日期型。P></

第一日:if the things first is then没有随机早期need for a number,你可以给你的任何操作数对MODS将have the same for possible number of each value of the collisions modulus。P></

but is not when things日期随机发生,那么奇怪。that is for example总是在考虑多值数据(10)。P></

我们使用find if mod 4:我们P></

10 mod 4=2P></

20 0 4=MODP></

30 mod 4=2P></

4=0 40)P></

MODS的2 4=50P></

3我知道possible from the values of the modulus(2 2 0)和一只会collisions,that is bad。P></

如果我们使用第一类:7号P></

mod 10 = 3 7P></

MODS组6 7 20P></

MODS的2 7=30P></

7=4 40 MODP></

50 mod 7=1P></

等P></

is not that we also笔记5人5 is the reason的第一选择,但我们是在is that of 5多重键。我们have to choose this means that number不分在原料选择原料,我们的钥匙,usually number是足够大的。P></

我知道erring on the reason of the前端有repetitive are used is to the numbers neutralize effect of the distribution in the keys的设立在学院collisions of a hash函数。P></


31也是特定于Java hash映射的,它使用int作为哈希数据类型。因此,最大容量为2^32。使用更大的费马或梅森素数是没有意义的。


它通常有助于在哈希桶中实现更均匀的数据传播,特别是对于低熵密钥。