为什么选取31作为乘数因子

计算字符串"code"的哈希值

ASCII值 A = 65 ---Z = 90, a = 97 --- z = 122

c = 99 o = 111 d = 100 e =101

参考计算公式:s [0] * 31 ^(n-1)+ s [1] * 31 ^(n-2)+ ... + s [n-1]

99 * 31^3 + 111 * 31^2 + 100 * 31 ^1 + 101 = 3059181

// 代码计算同上列手算

String str = "code";
System.out.println(str.hashCode()); // 3059181
// c = 99, o = 111, d = 100, e = 101
System.out.println('c'*31*31*31 + 'o'*31*31 +'d'*31 + 'e');  // 3059181

为什么是素数不是偶数

​ 假若乘数为10,那么以0结尾的整数,经过散列函数计算获得的哈希值可能会聚集在一起,产生冲突,还需要经过再散列或其他方法来解决冲突。

​ 在解决冲突的过程中,探针会索引没有利用的存储空间,来保存当前对象,因此降低了存储效率

为什么不可以是小一点的素数 2/3 呢

​ 同样是因为无法有效的进行散列,得到的hash值会聚集在一起

为什么不可以是101甚至更大的素数 233

​ 假如乘数因子是,使用上述散列函数计算有 99*101^3 = 101,999,799 只是计算四位字母的第一个字母,hash值就已经达到一亿了。

​ int整型的范围为-2,147,483,648~2,147,483,647。如果是较长的字符串,其计算结果会在整型范围内溢出,最终会导致数值信息丢失

因此只能选择不大不小的乘数因子

故选择 31, 33, 37, 39 和 41作为乘数,每个常数算出的哈希值冲突数都非常小。

如果对超过 50,000 个英文单词(由两个不同版本的 Unix 字典合并而成)进行 hash code 运算,并使用常数 31, 33, 37, 39 和 41 作为乘子,每个常数算出的哈希值冲突数都小于7个

为什么选择31作为乘数因子

JVM里最有效的计算方式就是进行位运算

而数字31有一个很好的特性,即乘法运算可以被移位和减法运算取代,来获取更好的性能

31 * i == (i << 5) - i

左移 << : 左边的最高位丢弃,右边补全0(把 << 左边的数据*2的移动次幂)。
右移 >> : 把>>左边的数据/2的移动次幂。
无符号右移 >>> : 无论最高位是0还是1,左边补齐0。

当 i = 2 时

左边:31 x 2 = 62

右边:(2 << 5) - 2 = 2 x 2 ^5 -2 = 62

JVM可以用此转换方式进行高效运算

原文地址:https://www.cnblogs.com/code-duck/p/13435706.html