根据Java文档,String对象的哈希代码是这样计算的:

S [0]*31^(n-1) + S [1]*31^(n-2) +…+ s (n - 1) 使用int算术,其中s[i]是 字符串的第i个字符,n是的长度 字符串,^表示取幂。

为什么用31作为乘数?

我知道乘数应该是一个相对较大的质数。那么为什么不是29岁,37岁,甚至97岁呢?


当前回答

通过相乘,位向左移位。这使用了更多哈希码的可用空间,减少了冲突。

通过不使用2的幂,低阶,最右边的位也被填充,与进入散列的下一段数据混合。

表达式n * 31等价于(n << 5) - n。

其他回答

根据Joshua Bloch的《Effective Java》(这本书再怎么推荐都不为过,多亏了stackoverflow上不断的提及,我才买了这本书):

选择值31是因为它是一个奇质数。如果它是偶数并且乘法溢出,信息就会丢失,因为乘2相当于移位。使用质数的优势不太明显,但它是传统的。31的一个很好的属性是乘法可以被移位和减法代替,以获得更好的性能:31 * i == (i << 5) - i。现代虚拟机自动进行这种优化。

(摘自第3章第9项:重写equals时总是重写hashcode,第48页)

我不确定,但我猜他们测试了一些质数样本,发现31在一些可能的字符串样本中给出了最好的分布。

Goodrich和Tamassia从超过50,000个英语单词(由Unix的两个变体提供的单词列表的并集组成)中计算出,使用常量31、33、37、39和41在每种情况下产生的碰撞将少于7次。这可能是如此多的Java实现选择此类常量的原因。

参见Java中的数据结构和算法的9.2节哈希表(第522页)。

Bloch并没有深入研究这个问题,但我总是听到/相信这是基本的代数。哈希可以归结为乘法和模运算,这意味着如果可以的话,永远不要使用带公因式的数字。换句话说,相对素数提供了答案的均匀分布。

使用哈希的数字通常是:

你放入的数据类型的模量 (2^32或2^64) 哈希表中桶数的模数(变化。在java中,以前是质数,现在是2^n) 在混合函数中乘以或平移一个神奇的数字 输入值

实际上,您只能控制其中的几个值,因此需要多加注意。

尼尔·科菲解释了为什么在熨平偏差下使用31。

基本上,使用31可以为哈希函数提供更均匀的集位概率分布。