簡體   English   中英

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

[英]Consistency of hashCode() on a Java string

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()在這個問題的上下文中完全不相關。

我可以看到早在Java 1.2的文檔。

雖然通常你不應該依賴哈希代碼實現保持不變,但它現在已經記錄了java.lang.String行為,因此更改它將被視為破壞現有契約。

在任何可能的情況下,你都不應該依賴於版本之間保持相同的哈希碼 - 但在我看來, java.lang.String是一個特例,因為算法被指定...只要你願意放棄當然,在指定算法之前與版本兼容。

我發現了一些關於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而不是哈希碼,你很高興 - 沒有討論也沒有后顧之憂......

您不應該依賴哈希碼等於特定值。 只是它會在同一個執行中返回一致的結果。 API文檔說明如下:

hashCode的一般契約是:

  • 每當在執行Java應用程序期間多次在同一對象上調用它時,hashCode方法必須始終返回相同的整數,前提是不修改對象的equals比較中使用的信息。 從應用程序的一次執行到同一應用程序的另一次執行,該整數不需要保持一致。

編輯由於String.hashCode()的javadoc指定了如何計算String的哈希碼,因此任何違反此規范的行都將違反公共API規范。

如上所述,通常您不應該依賴類的哈希碼保持不變。 請注意,即使在同一VM同一應用程序的后續運行也可能產生不同的哈希值。 AFAIK Sun JVM的哈希函數在每次運行時計算相同的哈希值,但這並不能保證。

請注意,這不是理論上的。 java.lang.String的哈希函數在JDK1.2中已更改 (舊哈希在分層字符串(如URL或文件名)方面存在問題,因為它傾向於為字符串生成相同的哈希值,這些字符串在末尾只有不同)。

java.lang.String是一個特例,因為hashCode()的算法已經(現在)記錄了,所以你可以依賴它。 我仍然認為這是不好的做法。 如果你需要一個帶有特殊記錄屬性的哈希算法,那就寫一個:-)。

要擔心的另一個(!)問題是Java的早期/晚期版本之間可能的實現更改。 我不相信實現細節是一成不變的,因此升級到未來的 Java版本可能會導致問題。

最重要的是,我不會依賴hashCode()的實現。

也許您可以通過使用此機制突出顯示您實際嘗試解決的問題,這將突出顯示更合適的方法。

只是回答你的問題而不是繼續討論。 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;
}

隨意檢查一下......

如果您擔心更改以及可能不兼容的VM,只需將現有的哈希碼實現復制到您自己的實用程序類中,然后使用它來生成哈希碼。

哈希碼將根據字符串中字符的ASCII值計算。

這是String Class中的實現如下

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相同的哈希碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM