当我们查看JDK源码或者一些有名的Java框架时,在一些hashcode的计算算法时都会出现31、29、17,那么为什么这样呢?这个数是不是可以任意的数字?
以下是一些框架,jdk源码中的hashcode的计算
//lucene8.11中的RAMFile类的hashcode计算
public class RAMFile{
//other code
public int hashCode() {
int h = (int) (length ^ (length >>> 32));
for (byte[] block : buffers) {
h = 31 * h + Arrays.hashCode(block);
}
return h;
}
}
//JDK中工具类的Arrays的计算hashcode
public class Arrays{
public static int hashCode(byte a[]) {
if (a == null)
return 0;
int result = 1;
for (byte element : a)
result = 31 * result + element;
return result;
}
}
Java中hashCode方法的设计经常涉及到将对象的某些字段值转换成整数,并且通常这些整数值会乘以一个质数(比如31),然后加上或减去其他字段的整数值。使用质数(特别是像31这样的小质数)的原因主要有以下几点:
- 减少冲突
质数在模运算下具有较好的分布特性,可以减少哈希冲突。哈希冲突是指不同的输入产生了相同的哈希值。使用质数作为乘数可以增加哈希值的分布范围,从而减少冲突的可能性。 - 性能考虑
31是一个比较小的质数,用31作为乘数在计算机运算中效率较高。具体来说,31 * i 相当于 (i << 5) - i,这是一个位移和减法的组合操作,在二进制层面是非常高效的。位移操作比乘法操作要快,而减法操作也相对较快。 - 避免整数溢出
Java中的hashCode方法返回的是int类型,其取值范围是-231到231-1。使用较小的质数如31作为乘数可以减小在计算过程中整数溢出的风险。