我一直在调查java中的hashCode()
方法,发现String类的方法很奇怪。源代码如下:
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;
}
代码本身非常简单。但我想知道以这种方式计算哈希码的原因是什么?
为什么选择31?
为什么从0开始而不是value.length - 1?
任何保证这会使哈希码更不可能相互碰撞?
答案 0 :(得分:4)
是的,哈希码冲突的概率非常低,例如在String的情况下,它取决于字符串值。如果我们没有使用new运算符创建任何String,那么如果新String与已存在的值相同,则不会创建新的String对象,它引用堆中的旧值,在这种情况下,只有hashCode的值将与预期相同。
hashCode的一般合约是:
每当在执行Java应用程序期间多次在同一对象上调用它时,hashCode方法必须始终返回相同的整数,前提是不修改对象的equals比较中使用的信息。从应用程序的一次执行到同一应用程序的另一次执行,此整数不需要保持一致。
从Java 1.2开始,java.lang.String类在字符串的整个文本中使用乘积和算法实现其hashCode()。[2]例如,给定java.lang.String类的实例,将具有由
定义的哈希码h(s)h(s)=s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
其中术语使用Java 32位int加法求和,s [i]表示字符串的第i个字符,n是s的长度。
您在Apache Harmony中的参考方法hashCode是:
public int hashCode() {
if (hashCode == 0) {
int hash = 0, multiplier = 1;
for (int i = offset + count - 1; i >= offset; i--) {
hash += value[i] * multiplier;
int shifted = multiplier << 5;
multiplier = shifted - multiplier;
}
hashCode = hash;
}
return hashCode;
}