Java字符串上hashCode()的一致性

时间:2009-04-24 09:11:05

标签: java string hashcode

Java String的hashCode值计算为(String.hashCode()):

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

是否存在以下表达式评估为false的任何情况(比如JVM版本,供应商等)?

boolean expression = "This is a Java string".hashCode() == 586653468

更新#1:如果您声称答案是“是,有这种情况” - 那么请举一个具体的例子说明“这是一个Java字符串”.hashCode()! = 586653468。尽可能具体/具体。

更新#2:我们都知道,依赖hashCode()的实现细节通常很糟糕。但是,我正在具体谈论String.hashCode() - 所以请将答案集中在String.hashCode()上。 Object.hashCode()在这个问题的上下文中完全不相关。

8 个答案:

答案 0 :(得分:95)

我可以看到文档早在Java 1.2。

虽然一般确实不应该依赖于哈希代码实现保持不变,但它现在已经记录了java.lang.String的行为,因此更改它会被视为违反现有合同

只要有可能,你就不应该依赖于版本之间保持相同的哈希码 - 但在我看来java.lang.String是一个特例,因为算法已经指定了。只要你愿意在指定算法之前放弃与版本的兼容性,当然。

答案 1 :(得分:18)

我发现了一些关于JDK 1.0和1.1以及> = 1.2:

的内容
  

在JDK 1.0.x和1.1.x中使用hashCode   功能很长的字符串工作   每第n个字符采样。这个   非常保证你会有   很多字符串都是一样的   值,从而减慢Hashtable   抬头。在JDK 1.2中,该函数具有   经过改进,可以使结果倍增   到目前为止31岁然后添加下一个   字符顺序。这是一个   稍微慢一点,但要好得多   避免碰撞。   资料来源:http://mindprod.com/jgloss/hashcode.html

有些不同,因为你似乎需要一个数字:如何使用CRC32或MD5代替哈希码,你很高兴 - 没有讨论,也没有后顾之忧......

答案 2 :(得分:8)

您不应该依赖哈希码等于特定值。只是它会在同一个执行中返回一致的结果。 API文档说明如下:

  

hashCode的一般合约是:

     
      
  • 每当在执行Java应用程序期间多次在同一对象上调用它时,hashCode方法必须始终返回相同的整数,前提是不修改对象的equals比较中使用的信息。从应用程序的一次执行到同一应用程序的另一次执行,此整数不需要保持一致。
  •   

修改 由于String.hashCode()的javadoc指定了如何计算String的哈希码,因此违反此规范将违反公共API规范。

答案 3 :(得分:4)

如上所述,通常你不应该依赖类的哈希码保持不变。请注意,即使在相同VM 上运行相同应用程序,也可能产生不同的哈希值。 AFAIK Sun JVM的哈希函数在每次运行时计算相同的哈希值,但这不能保证。

请注意,这不是理论上的。 JDK1.2中java.lang.String was changed的哈希函数(旧哈希在URL或文件名之类的分层字符串方面存在问题,因为它倾向于为字符串生成相同的哈希值,最后只有不同的字符串)

java.lang.String是一种特殊情况,因为hashCode()的算法已经(现在)记录,所以你可以依赖它。我仍然认为这是不好的做法。如果您需要具有特殊文档属性的哈希算法,只需编写一个: - )。

答案 4 :(得分:3)

担心的另一个(!)问题是Java的早期/晚期版本之间可能的实现更改。我不相信实现细节是一成不变的,因此升级到 future Java版本可能会导致问题。

底线是,我不会依赖hashCode()的实现。

也许您可以通过使用此机制突出显示您实际尝试解决的问题,这将突出显示更合适的方法。

答案 5 :(得分:2)

回答你的问题而不是继续讨论。 Apache Harmony JDK实现似乎使用了不同的算法,至少它看起来完全不同:

Sun JDK

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;
}

Apache Harmony

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;
}

随意自行检查......

答案 6 :(得分:2)

如果您担心更改以及可能不兼容的VM,只需将现有的哈希码实现复制到您自己的实用程序类中,然后使用它来生成哈希码。

答案 7 :(得分:0)

哈希码将根据字符串中字符的ASCII值进行计算。

这是String类中的实现,如下所示

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        hash = h = isLatin1() ? StringLatin1.hashCode(value)
                              : StringUTF16.hashCode(value);
    }
    return h;
}

哈希码中的冲突是不可避免的。例如,字符串“ Ea”和“ FB”的哈希码与2236相同