可能重复:
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相当于移位。使用素数的优势不太明显,但它是传统的。
答案 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不会。