Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么在哈希代码中使用素数?_Java_Hashcode_Primes - Fatal编程技术网

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产生良好的分布,具有
  • 速度上的合理权衡

因为您希望将要乘以的数字和要插入的存储桶的数量进行正交素数分解

假设有8个桶要插入。如果要乘以的数字是8的某个倍数,则插入的bucket将仅由最不重要的条目(根本不乘以的条目)确定。类似的条目将发生冲突。对于散列函数来说不太好


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,这允许按位优化
下面是第9项的完整引用:当您重写
等于时,始终重写
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倍