为什么选择31在hashcode()实现中进行乘法?

时间:2012-07-18 08:15:35

标签: java algorithm hashcode

  

可能重复:
  Why does Java's hashCode() in String use 31 as a multiplier?
  Why use a prime number in hashCode?

Effective Java 项目9:覆盖等于时始终覆盖hashCode 请考虑以下相关代码段,该代码段覆盖Object类中定义的hashcode()。

public final class PhoneNumber {
  private final short areaCode;
  private final short prefix;
  private final short lineNumber;
  ......//Rest ignored on purpose
  .....

private volatile int hashCode; // (See Item 71)
@Override public int hashCode() {
   int result = hashCode;
   if (result == 0) {
    result = 17;
    result = 31 * result + areaCode;
    result = 31 * result + prefix;
    result = 31 * result + lineNumber;
    hashCode = result;
   }
 return result;
 }
}

我理解为什么选择非零初始值“17”。我也理解乘法是在每一步中完成的,因此字段的顺序在计算hashcode()中起着重要作用。但我不明白选择31作为乘法值的原因。有人可以向我解释原因吗?这就是布洛赫对31所说的话,但我并没有真正理解。我特别不理解下面的斜体字。

  

选择值31是因为它是奇数素数。 如果它是偶数并且乘法溢出,则信息将丢失,因为乘以2相当于移位。使用素数的优势不太明显,但它是传统的。

3 个答案:

答案 0 :(得分:10)

向右移动只会在右侧引入一个零,并在数字的二进制表示的左侧丢失一点,因此这是一个明显的信息丢失。重复此过程会逐渐丢失从早期计算中累积的所有信息。这意味着输入哈希码计算的字段越多,对早期字段的最终结果的影响就越小。

答案 1 :(得分:3)

他使用素数的原因是它更有可能产生随机模式。例如,如果你使用9,那么你可以用3的倍数来重叠。

AFAIK 31用于字符串,因为字母表中少于31个字母,这意味着最多6个字母的所有单词都有唯一的哈希码。例如,如果使用61(素数小于64),最多5个字母将产生唯一代码,如果使用13(素数小于16),则可以使用两个字母单词进行碰撞。

答案 2 :(得分:1)

我将描述不同数字的答案,但我怀疑推理是相似的。对字符X的哈希值的贡献是X * B ^ k,其中B在您的情况下是31,并且k取决于字符串中X的位置及其长度。该算法通常以字大小为模。出于这个原因,我们希望B ^ k对于不同的k值是不同的。

现在,在Gonnet和Baeza-Yates的“算法和数据结构手册”第3.3.1节“实用散列函数”中,他们说“对于这个函数,建议使用值B = 131,因为B ^ i有一个最大值循环mod 2 ^ k,8 <= k <= 64。“我想知道周期长度31有mod 2 ^ 32?我相信31将适合Sparc立即操作数,但131不会。