为什么Java中的String.hashCode()有很多冲突?[已关闭]
为什么 String.hashcode() 有这么多冲突?
我正在阅读jdk1.6中的String.hashCode(),下面是代码
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
这在我看来是相当令人困惑的,因为它有太多的冲突;虽然它不需要是唯一的(我们仍然可以依赖equals()),但更少的冲突意味着更好的性能,而无需访问链表中的条目。
假设我们有两个字符,那么只要我们能找到两个匹配的字符串,那么我们将有相同的哈希码()
a * 31 +b = c * 31 +d
很容易得出结论,举一个简单的例子是使a-c = 1和d-b = 31;所以我写了下面的代码用于简单测试(a-c) * 31 = d-b
public void testHash() {
System.out.println("A:" + (int)'A');
System.out.println("B:" + (int)'B');
System.out.println("a:" + (int)'a');
System.out.println("Aa".hashCode() + "," + "BB".hashCode());
System.out.println("Ba".hashCode() + "," + "CB".hashCode());
System.out.println("Ca".hashCode() + "," + "DB".hashCode());
System.out.println("Da".hashCode() + "," + "EB".hashCode());
}
它将在下面打印结果,这意味着所有字符串都具有相同的哈希码(),并且很容易在循环中执行此操作。
A:65
B:66
a:97
2112,2112
2143,2143
2174,2174
2205,2205
更糟糕的是,假设我们在字符串中有4个字符,根据算法,假设前2个字符产生a2,第2个2个字符产生b2;哈希码仍然是这样的,当a2和b2等于2个字符串之间时,我们将得到更多具有哈希码()冲突的字符串。这样的例子是“AaAa”,“BBBB”等;那么我们将有6个字符,8个字符......a2 * 31^2 + b2
假设大多数时候我们在字符串中使用ascii表中的字符,这些字符将在哈希映射或哈希表中使用,那么这里选择的素数31肯定太小了;
一个简单的解决方法是使用一个更大的素数(幸运的是,257是一个素数),这可以避免这种冲突。当然,如果字符串很长,选择太大的数字会导致返回的int值溢出,但我假设大多数时候用作键的字符串不是那么大?当然,它仍然可以返回一个长整型值来避免这种情况。
以下是我的 betterhash() 的修改版本,它可以通过运行它将在值以下打印的代码来轻松解决此类冲突,这对于解决此问题是有效的。
16802,17028
17059,17285
17316,17542
17573,17799
但是为什么jdk不修复它?感谢。
@Test
public void testBetterhash() {
System.out.println(betterHash("Aa") + "," + betterHash("BB"));
System.out.println(betterHash("Ba") + "," + betterHash("CB"));
System.out.println(betterHash("Ca") + "," + betterHash("DB"));
System.out.println(betterHash("Da") + "," + betterHash("EB"));
}
public static int betterHash(String s) {
int h = 0;
int len = s.length();
for (int i = 0; i < len; i++) {
h = 257*h + s.charAt(i);
}
return h;
}