根据Java文档,String对象的哈希代码是这样计算的:
S [0]*31^(n-1) + S [1]*31^(n-2) +…+ s (n - 1) 使用int算术,其中s[i]是 字符串的第i个字符,n是的长度 字符串,^表示取幂。
为什么用31作为乘数?
我知道乘数应该是一个相对较大的质数。那么为什么不是29岁,37岁,甚至97岁呢?
根据Java文档,String对象的哈希代码是这样计算的:
S [0]*31^(n-1) + S [1]*31^(n-2) +…+ s (n - 1) 使用int算术,其中s[i]是 字符串的第i个字符,n是的长度 字符串,^表示取幂。
为什么用31作为乘数?
我知道乘数应该是一个相对较大的质数。那么为什么不是29岁,37岁,甚至97岁呢?
当前回答
Goodrich和Tamassia从超过50,000个英语单词(由Unix的两个变体提供的单词列表的并集组成)中计算出,使用常量31、33、37、39和41在每种情况下产生的碰撞将少于7次。这可能是如此多的Java实现选择此类常量的原因。
参见Java中的数据结构和算法的9.2节哈希表(第522页)。
其他回答
根据Joshua Bloch的《Effective Java》(这本书再怎么推荐都不为过,多亏了stackoverflow上不断的提及,我才买了这本书):
选择值31是因为它是一个奇质数。如果它是偶数并且乘法溢出,信息就会丢失,因为乘2相当于移位。使用质数的优势不太明显,但它是传统的。31的一个很好的属性是乘法可以被移位和减法代替,以获得更好的性能:31 * i == (i << 5) - i。现代虚拟机自动进行这种优化。
(摘自第3章第9项:重写equals时总是重写hashcode,第48页)
通过相乘,位向左移位。这使用了更多哈希码的可用空间,减少了冲突。
通过不使用2的幂,低阶,最右边的位也被填充,与进入散列的下一段数据混合。
表达式n * 31等价于(n << 5) - n。
事实上,37就可以了!Z:= 37 * x可计算为y:= x + 8 * x;z:= x + 4 * y。这两个步骤都对应一个LEA x86指令,所以这是非常快的。
事实上,通过设置y:= x + 8 * x,可以以同样的速度完成与更大的素数73的乘法运算;Z:= x + 8 * y。
使用73或37(而不是31)可能会更好,因为它会导致更密集的代码:两条LEA指令只占用6个字节,而move+shift+subtract(31的乘法)占用7个字节。一个可能的警告是,这里使用的3参数LEA指令在英特尔的Sandy桥架构上变慢了,延迟增加了3个周期。
而且,73是谢尔顿·库珀最喜欢的数字。
Bloch并没有深入研究这个问题,但我总是听到/相信这是基本的代数。哈希可以归结为乘法和模运算,这意味着如果可以的话,永远不要使用带公因式的数字。换句话说,相对素数提供了答案的均匀分布。
使用哈希的数字通常是:
你放入的数据类型的模量 (2^32或2^64) 哈希表中桶数的模数(变化。在java中,以前是质数,现在是2^n) 在混合函数中乘以或平移一个神奇的数字 输入值
实际上,您只能控制其中的几个值,因此需要多加注意。
You can read Bloch's original reasoning under "Comments" in http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4045622. He investigated the performance of different hash functions in regards to the resulting "average chain size" in a hash table. P(31) was one of the common functions during that time which he found in K&R's book (but even Kernighan and Ritchie couldn't remember where it came from). In the end he basically had to choose one and so he took P(31) since it seemed to perform well enough. Even though P(33) was not really worse and multiplication by 33 is equally fast to calculate (just a shift by 5 and an addition), he opted for 31 since 33 is not a prime:
剩下的 第四,我可能会选择P(31),因为它是RISC上最便宜的计算方法 机器(因为31是2的2次幂之差)P (33) 同样,计算起来很便宜,但它的性能稍微差一些,并且 33是合数,这让我有点紧张。
因此,推理并不像这里的许多答案所暗示的那样理性。但我们都善于在做出直觉决定后提出理性的理由(甚至布洛赫也可能倾向于这样)。