String.hashCode()源码如下:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
从源码中我们可以看出:
- String有一个私有变量hash来缓存哈希值,即当该串第一次调用hashCode()方法时,hash默认值为0,继续执行,当字符串长度大于0时计算出一个哈希值赋给hash,之后再调用hashCode()方法时不会重新计算,直接返回hash;
- 计算时,使用的是该字符串截成的一个字符数组,用每个字符的ASCII值进行计算,根据注释可以看出哈希计算公式是:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],n是字符数组的长度,s是字符数组;
- 算法中还有一个乘数31,为什么使用31呢?
hash函数必须选用质数,这是被科学家论证过的hash函数减少冲突的理论;- 如果乘数是偶数,并且乘法溢出的话,信息就会丢失,因为使用偶数相当于位移运算(低位补0);
- 31 * i 可以用 (i << 5) - i 来计算,而移位操作的效率高于乘法,所以这是基于性能角度的考虑;
- 31是个不大不小的质数,兼顾了性能和冲突率,太小hash冲突概率大,太大过于分散占用存储空间大,所以选择一个不大不小的质数很有必要。
对于字符串"hashcode"的哈希值计算过程如下: 初始化h为0。 对于字符'h',计算h = 31 * 0 + 'h' = 104。 对于字符'a',计算h = 31 * 104 + 'a' = 3295。 对于字符's',计算h = 31 * 3295 + 's' = 102010。 对于字符'h',计算h = 31 * 102010 + 'h' = 3162931。 对于字符'c',计算h = 31 * 3162931 + 'c' = 97977461。 对于字符'o',计算h = 31 * 97977461 + 'o' = 3035438611。 对于字符'd',计算h = 31 * 3035438611 + 'd' = 94176018441。 对于字符'e',计算h = 31 * 94176018441 + 'e' = 2922100094061。 返回最终的哈希值h = 2922100094061。 因此,字符串"hashcode"的hashCode为2922100094061。请注意,hashCode的计算过程可能因为具体的实现而有所不同,上述过程仅为演示目的。