热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

为什么在hashCode中使用素数?-WhyuseaprimenumberinhashCode?

IwasjustwonderingwhyisthatprimesareusedinaclassshashCode()method?Forexample,whenu

I was just wondering why is that primes are used in a class's hashCode() method? For example, when using Eclipse to generate my hashCode() method there is always the prime number 31 used:

我想知道为什么在类的hashCode()方法中使用了质数?例如,当使用Eclipse生成hashCode()方法时,总是使用质数31:

public int hashCode() {
     final int prime = 31;
     //...
}

References:

引用:

Here is a good primer on Hashcode and article on how hashing works that I found (C# but the concepts are transferrable): Eric Lippert's Guidelines and rules for GetHashCode()

这里有一篇关于哈希代码的入门文章和关于哈希如何工作的文章(c#但是概念是可转移的):Eric Lippert的GetHashCode()的指导方针和规则

8 个解决方案

#1


83  

Because you want the number you are multiplying by and the number of buckets you are inserting into to have orthogonal prime factorizations.

因为你想要你乘以的数和你要插入的桶数来得到正交的质因数分解。

Suppose there are 8 buckets to insert into. If the number you are using to multiply by is some multiple of 8, then the bucket inserted into will only be determined by the least significant entry (the one not multiplied at all). Similar entries will collide. Not good for a hash function.

假设有8个桶要插入。如果要相乘的数字是8的某个倍数,那么插入的桶将只由最不重要的条目(根本没有相乘的条目)决定。类似的条目将碰撞。不适合哈希函数。

31 is a large enough prime that the number of buckets is unlikely to be divisible by it (and in fact, modern java HashMap implementations keep the number of buckets to a power of 2).

31是一个足够大的素数,所以桶的数量不太可能被它整除(实际上,现代java HashMap实现将桶的数量保持为2的幂)。

#2


113  

Prime numbers are chosen to best distribute data among hash buckets. If the distribution of inputs is random and evenly spread, then the choice of the hash code/modulus does not matter. It only has an impact when there is a certain pattern to the inputs.

选择素数以便在哈希桶之间最好地分配数据。如果输入的分布是随机的、均匀分布的,那么选择散列码/模数并不重要。只有当输入有特定的模式时才会产生影响。

This is often the case when dealing with memory locations. For example, all 32-bit integers are aligned to addresses divisible by 4. Check out the table below to visualize the effects of using a prime vs. non-prime modulus:

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

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

Notice the almost-perfect distribution when using a prime modulus vs. a non-prime modulus.

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

However, although the above example is largely contrived, the general principle is that when dealing with a pattern of inputs, using a prime number modulus will yield the best distribution.

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

#3


21  

For what it's worth, Effective Java 2nd Edition hand-waives around the mathematics issue and just say that the reason to choose 31 is:

值得注意的是,有效的Java第二版围绕着数学问题手把手地说,选择31的原因是:

  • Because it's an odd prime, and it's "traditional" to use primes
  • 因为它是一个奇数素数,使用素数是“传统的”
  • It's also one less than a power of two, which permits for bitwise optimization
  • 它也比2的幂小1,这允许位优化

Here's the full quote, from Item 9: Always override hashCode when you override equals:

下面是第9项的完整引用:当你重写=时,总是重写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.

之所以选择31是因为它是一个奇数素数。如果它是偶数并且乘法溢出,信息就会丢失,因为乘法2等同于移位。使用素数的优势不那么明显,但它是传统的。

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

31日是一个很好的属性,乘法可以被转变为更好的性能(§15.19)和减法:

 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.

虽然本项目中的菜谱产生了相当好的散列函数,但它并没有生成最先进的散列函数,Java平台库也没有提供1.6版那样的散列函数。写这样的哈希函数是一个研究课题,最好留给数学家和理论计算机科学家。

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.

平台的后续版本可能会为它的类和实用方法提供最先进的哈希函数,以允许普通程序员构建此类哈希函数。与此同时,本项目中所描述的技术应该适用于大多数应用程序。

Rather simplistically, it can be said that using a multiplier with numerous divisors will result in more hash collisions. Since for effective hashing we want to minimize the number of collisions, we try to use a multiplier that has fewer divisors. A prime number by definition has exactly two distinct, positive divisors.

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

Related questions

  • Java hashCode from one field - the recipe, plus example of using Apache Commons Lang's builders
  • 来自一个字段的Java hashCode -食谱,加上使用Apache Commons Lang的生成器的示例
  • is it incorrect to define an hashcode of an object as the sum, multiplication, whatever, of all class variables hashcodes?
  • 将一个对象的hashcode定义为所有类变量hashcodes的和、乘法、等等,是不正确的吗?
  • Absolute Beginner's Guide to Bit Shifting?
  • 绝对初学者的钻头移位指南?

#4


4  

I heard that 31 was chosen so that the compiler can optimize the multiplication to left-shift 5 bits then subtract the value.

我听说31被选择了,这样编译器就可以将乘法优化为左移5位,然后减去这个值。

#5


2  

Here's a citation a little closer to the source.

这里有一个引文更接近源。

It boils down to:

它可以归结为:

  • 31 is prime, which reduces collisions
  • 31是质数,可以减少碰撞
  • 31 produces a good distribution, with
  • 31产生一个良好的分布,与
  • a reasonable tradeoff in speed
  • 在速度上的合理权衡

#6


2  

First you compute the hash value modulo 2^32 (the size of an int), so you want something relatively prime to 2^32 (relatively prime means that there are no common divisors). Any odd number would do for that.

首先计算出散列值模2 ^ 32(int)的大小,所以你想要相对' 2 ^ 32(互质意味着没有公共因子)。任何奇数都可以。

Then for a given hash table the index is usually computed from the hash value modulo the size of the hash table, so you want something that is relatively prime to the size of the hash table. Often the sizes of hash tables are chosen as prime numbers for that reason. In the case of Java the Sun implementation makes sure that the size is always a power of two, so an odd number would suffice here, too. There is also some additional massaging of the hash keys to limit collisions further.

然后对于给定的哈希表,索引通常由哈希值模块计算哈希表的大小,因此您需要一个相对于哈希表大小的素数。通常由于这个原因,哈希表的大小被选择为素数。在Java的例子中,Sun实现确保大小总是2的乘方,所以在这里一个奇数也足够了。还有一些哈希键的附加修改,以进一步限制冲突。

The bad effect if the hash table and the multiplier had a common factor n could be that in certain circumstances only 1/n entries in the hash table would be used.

如果哈希表和乘数有一个公因式n,那么不好的影响可能是在某些情况下,在哈希表中只使用1/n项。

#7


0  

It generally helps achieve a more even spread of your data among the hash buckets, especially for low-entropy keys.

它通常有助于在hash bucket中实现更均匀的数据分布,特别是对于低熵的键。

#8


0  

31 is also specific to Java HashMap which uses a int as hash data type. Thus the max capacity of 2^32. There is no point in using larger Fermat or Mersenne primes.

31也是特定于Java HashMap的,它使用int作为散列数据类型。因此,最大容量为2 ^ 32。使用更大的Fermat或Mersenne素数是没有意义的。


推荐阅读
  • 标题: ... [详细]
  • 如何使用计算机控制遥控车的步骤和电路制作方法
    本文介绍了使用计算机控制遥控车的步骤和电路制作方法。首先,需要检查发送器的连接器和跳线,以确定命令的传递方式。然后,通过连接跳线和地面,将发送器与电池的负极连接,以实现遥控车的前进。接下来,制作一个简单的电路,使用Arduino命令将连接到跳线的电线接地,从而实现将Arduino命令转化为发送器命令。最后,通过焊接晶体管和电阻,完成电路制作。详细的步骤和材料使用方法将在正文中介绍。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 本文介绍了一种解析GRE报文长度的方法,通过分析GRE报文头中的标志位来计算报文长度。具体实现步骤包括获取GRE报文头指针、提取标志位、计算报文长度等。该方法可以帮助用户准确地获取GRE报文的长度信息。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
author-avatar
hcl春丽
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有