Java 为什么在哈希代码中使用素数?
我只是想知道为什么在类的Java 为什么在哈希代码中使用素数?,java,hashcode,primes,Java,Hashcode,Primes,我只是想知道为什么在类的hashCode()方法中使用素数?例如,当使用Eclipse生成我的hashCode()方法时,总会使用素数31: public int hashCode() { final int prime = 31; //... } 参考文献: 下面是一篇关于Hashcode的优秀入门文章,以及我发现的关于hashing如何工作的文章(C#但是概念是可转移的): 我听说选择了31,这样编译器就可以优化乘法到左移5位,然后减去值。它通常有助于在散列桶中实现更均
hashCode()
方法中使用素数?例如,当使用Eclipse生成我的hashCode()
方法时,总会使用素数31
:
public int hashCode() {
final int prime = 31;
//...
}
参考文献:
下面是一篇关于Hashcode的优秀入门文章,以及我发现的关于hashing如何工作的文章(C#但是概念是可转移的):
我听说选择了31,这样编译器就可以优化乘法到左移5位,然后减去值。它通常有助于在散列桶中实现更均匀的数据分布,尤其是对于低熵键。这里有一点接近源代码 归结起来是:
- 31是素数,这减少了碰撞
- 31产生良好的分布,具有
- 速度上的合理权衡
31是一个足够大的素数,因此存储桶的数量不可能被它整除(事实上,现代java HashMap实现将存储桶的数量保持为2的幂次方)。选择素数是为了在散列存储桶之间最佳地分配数据。如果输入的分布是随机且均匀分布的,则哈希码/模的选择无关紧要。只有当输入有某种模式时,它才会产生影响 在处理内存位置时,通常会出现这种情况。例如,所有32位整数都与可被4整除的地址对齐。查看下表以可视化使用素数与非素数模量的效果:
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
注意当使用素数模与非素数模时,几乎完美的分布
然而,尽管上面的示例在很大程度上是人为设计的,但一般原则是,在处理输入模式时,使用质数模将产生最佳分布。值得一提的是,《有效Java第二版》放弃了数学问题,只说选择31的原因是:
- 因为它是一个奇怪的素数,使用素数是“传统的”
- 它也比2的幂小1,这允许按位优化
等于时,始终重写hashCode
:
选择值31是因为它是奇数素数。如果它是偶数并且乘法溢出,信息就会丢失,因为乘2等于移位。使用素数的优点不太明显,但它是传统的
31的一个很好的特性是乘法可以用移位()和减法代替,以获得更好的性能:
31 * i == (i << 5) - i
31*i==(i首先计算散列值模2^32(一个int
的大小),所以你想要一个相对素数为2^32的值(相对素数意味着没有公约数)。任何奇数都可以
然后,对于给定的哈希表,索引通常是根据哈希值乘以哈希表的大小来计算的,因此您需要一些与哈希表的大小相对素数的内容。通常,出于这个原因,哈希表的大小被选为素数。在Java的情况下,Sun实现确保大小始终为素数二的幂,所以奇数在这里也足够了。还有一些额外的哈希键按摩来进一步限制冲突
如果哈希表和乘法器有一个公共因子,那么坏的影响可能是,在某些情况下,哈希表中只会使用1/n个条目。31也特定于Java HashMap,它使用int作为哈希数据类型。因此,最大容量为2^32。使用更大的Fermat或Mersenne素数没有意义 使用素数的原因是当数据显示某些特定模式时,尽量减少冲突
第一件事:如果数据是随机的,那么就不需要素数,你可以对任何数字进行mod运算,对于模的每个可能值,你将有相同数量的碰撞
但是当数据不是随机的,那么奇怪的事情就会发生。例如,考虑数字数据总是10的倍数。< /P>
如果我们使用mod 4,我们会发现:
10模4=2
20模4=0
30模4=2
40模4=0
50模4=2
所以从模量(0,1,2,3)的3个可能值来看,只有0和2会发生碰撞,这是不好的
如果我们使用像7这样的素数:
10模7=3
20模7=6
30模7=2
40模7=4
50模7=1
等
我们还注意到5不是一个好的选择,但5是质数。原因是我们所有的键都是5的倍数。这意味着我们必须选择一个不除以键的质数,选择一个大的质数通常就足够了
由于重复的原因,使用素数是为了中和散列函数碰撞中键中的模式的影响。
然后散列31的散列函数将不理想地执行。然而,考虑到COMM,我认为这样的哈希表实现设计得很差。31作为乘法器。因此选择31是基于这样的假设,即哈希表实现者知道31通常用于哈希代码?选择31是基于这样的想法,即大多数实现具有相对较小的素数的因式分解。通常为2、3和5。它可能从10开始,当它达到时会增长3倍