为什么以下三个字符串的哈希码相同?

时间:2018-11-05 13:35:31

标签: java string hashcode

阅读JDK的源代码后,我仍然对字符串感到惊讶 "AaAa", "AaBB" and "BBBB"具有相同的哈希码。

JDK的来源如下,

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;

任何人都可以澄清吗?

5 个答案:

答案 0 :(得分:5)

因为the hash code is defined to be calculated for a String就是这样:

  

字符串对象的哈希码计算为

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

所以:

  • 对于AaAa65*31^3 + 97*31^2 + 65*31 + 97 = 2031744
  • 对于AaBB65*31^3 + 97*31^2 + 66*31 + 66 = 2031744
  • 对于BBBB66*31^3 + 66*31^2 + 66*31 + 66 = 2031744

答案 1 :(得分:5)

因为probability

大约有40亿个哈希码(Integer.MIN_VALUE -> Integer.MAX_VALUE)和基本上无限的字符串。必然会有collisions。实际上,the birthday problem向我们展示了that only ~77,000 strings是发生任意冲突的高概率所必需的-这就是说,如果散列函数具有极高的熵(不是)。

也许您在想一个cryptographic hash function,其中

  

对消息进行少量更改应更改哈希值,因此   新哈希值似乎与旧哈希值不相关   哈希值

在这种情况下,Object.hashCode并非用于加密目的。

另请参阅How secure is Java's hashCode()?

答案 2 :(得分:3)

他们的哈希码是

AaAa: ((65 * 31 + 97) * 31 + 65) * 31 + 97 = 2.031.744
AaBB: ((65 * 31 + 97) * 31 + 66) * 31 + 66 = 2.031.744
BBBB: ((66 * 31 + 66) * 31 + 66) * 31 + 66 = 2.031.744

这就是数学原理,没什么可混淆的。
请注意,97和66之间恰好是31的区别,这就是使这些哈希码排列得如此好的原因。

答案 3 :(得分:1)

这是Java文档中Dict { key_equivalent = enabled_context_menu = false enabled_services_menu = false } 方法的说明:

  

只要在执行Java应用程序期间在同一个对象上多次调用它,Object#hashCode方法就必须一致地返回相同的整数,前提是没有hashCode比较中使用的信息修改。从一个应用程序的一个执行到同一应用程序的另一个执行,此整数不必保持一致。

     

如果根据equals方法两个对象相等,则在两个对象中的每个对象上调用equals(Object)方法必须产生相同的整数结果。

     

根据hashCode方法,如果两个对象不相等,则不需要在两个对象中的每一个上调用java.lang.Object#equals(java.lang.Object)方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

因此,hashCode类的实现也保持了上述特征。这是正常现象。

答案 4 :(得分:0)

有几种类型的哈希函数具有不同的设计和性能标准。

  1. 用于索引的哈希函数(例如关联数组和类似用法)可能会发生频繁冲突而不会出现问题,因为哈希表代码随后将在某些命名器中处理该问题,例如将它们放入列表或重新哈希。这就是时间的性能。 Java hash()似乎是这种类型

  2. 另一种类型的函数,例如SHA *等加密哈希,以哈希性能为代价,努力避免冲突。

  3. 第三种哈希函数是密码验证器哈希,它设计得非常慢(通常为100毫秒左右),可能需要大量内存,因此不常发生冲突不是问题。这里的目的是要使蛮力攻击花费尽可能长的时间。

一次根据使用情况选择哈希的类型和特征。