簡體   English   中英

如何為 Java 中的 String 對象生成 hashCode()?

[英]How hashCode() is generated for a String object in Java?

我們知道對象的 hashCode() 方法會根據對象實例的內存地址給出哈希碼。 因此,當我們有兩個具有相同數據的同一類的對象時,它仍然會給出不同的哈希碼,因為它們存儲在不同的內存位置。

現在,當我們使用 new String("Some_Name") 創建兩個字符串對象時,我們將有兩個對象存儲在不同的地址中。 當我們看到這兩個對象的哈希碼時,我們應該得到不同的哈希碼,因為它們存儲在不同的內存位置。 但我們最終會得到與結果相同的哈希碼。

Employee empObject = new Employee("Some_Name");
Employee empObject1 =new Employee("Some_Name");
String stringObject= new String("Some_Name");
String stringObject1=new String("Some_Name");
                                                  //Output
System.out.println(empObject.hashCode());     //1252169911
System.out.println(empObject1.hashCode());    //2101973421
System.out.println(stringObject.hashCode());  //1418906358
System.out.println(stringObject1.hashCode()); //1418906358

這是否意味着 String 對象已經覆蓋了 Object.hashCode() 方法。 如果在被覆蓋的方法中是這樣,它必須在堆中搜索具有相同數據的其他 String 對象,並為所有對象放置一個常量 hashCode。 如果我的基礎知識本身是錯誤的,請幫助我。

注意:這與字符串字面量無關,實際上它與字符串對象有關,因為字面量存儲在字符串常量池中,而字符串對象是在堆內的池外作為對象創建的。

這是我通過簡單的谷歌搜索找到的:

對象的哈希碼允許算法和數據結構將對象放入隔間,就像打印機類型盒中的字母類型一樣。 打印機將所有A類型放入A的隔間中,他只在這個隔間中尋找A 這個簡單的系統讓他找到類型比在未分類的抽屜中搜索要快得多。 這也是基於哈希的集合的想法,例如HashMapHashSet

合同在 hashCode 方法的JavaDoc中進行了解釋。 大致可以用這句話來概括:

相等的對象在運行的進程中必須具有相同的哈希碼

  • 不相等的對象必須有不同的哈希碼——錯誤
  • 具有相同哈希碼的對象必須相等——錯誤

在此處輸入圖像描述

該合約允許不相等的對象共享相同的哈希碼,例如上圖中的Aµ對象。 在數學術語中,從對象到哈希碼的映射不必是單injective的,甚至不必是bijective的。 這很明顯,因為可能的不同對象的數量通常大於可能的哈希碼 2^32 的數量。

希望能幫助到你

解釋

您的第一段描述了hashCode默認行為。 但通常類會覆蓋它並創建基於內容的解決方案(與equals相同)。 這尤其適用於String類。


默認hashCode

默認實現不是在Java中完成,而是直接在JVM中實現,它有一個native關鍵字。 您始終可以使用System#identityHashCode獲得原始hashCode ,請參閱其文檔

為給定對象返回與默認方法hashCode()返回相同的哈希碼,無論給定對象的類是否覆蓋hashCode() 空引用的哈希碼為零。

請注意, hashCode的默認實現不一定基於內存位置。 它通常是相關的,但您決不能依賴它(請參閱How is hashCode() computed in Java )。 這是Object#hashCode文檔

返回對象的哈希碼值。 支持這種方法是為了有利於哈希表,例如 HashMap 提供的那些。

hashCode 的一般合約是:

  • 每當在 Java 應用程序執行期間對同一個對象多次調用它時,hashCode 方法必須始終返回相同的整數,前提是沒有修改對象上的 equals 比較中使用的信息。 該整數不需要從應用程序的一次執行到同一應用程序的另一次執行保持一致。
  • 如果兩個對象根據 equals(Object) 方法相等,則對兩個對象中的每一個調用 hashCode 方法必須產生相同的整數結果。
  • 如果根據 equals(java.lang.Object) 方法,如果兩個對象不相等,則不需要對兩個對象中的每一個調用 hashCode 方法都必須產生不同的整數結果。 但是,程序員應該意識到,為不相等的對象生成不同的整數結果可能會提高哈希表的性能。

相關部分是第二個和第三個要求。 它的行為必須與equals相同,並且哈希沖突是可以的(但不是最佳的)。

Object#equals通常用於創建自定義的基於內容的比較(請參閱文檔)。


字符串hashCode

現在讓我們看一下String#hashCode的實現。 如前所述,該類覆蓋該方法並實現基於內容的解決方案。 因此"hello"的哈希值將始終與"hello" hello" 的哈希值相同。 即使您使用構造函數強制新實例:

// Will have the same hash
new String("hello").hashCode()
new String("hello").hashCode()

它的工作原理與equals完全一樣,這里也將輸出true

new String("hello").equals(new String("hello")) // true

根據hashCode方法的合同要求(請參閱文檔)。

這是該方法的實現(JDK 10):

/**
 * Returns a hash code for this string. The hash code for a
 * {@code String} object is computed as
 * <blockquote><pre>
 * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 * </pre></blockquote>
 * using {@code int} arithmetic, where {@code s[i]} is the
 * <i>i</i>th character of the string, {@code n} is the length of
 * the string, and {@code ^} indicates exponentiation.
 * (The hash value of the empty string is zero.)
 *
 * @return  a hash code value for this object.
 */
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        hash = h = isLatin1() ? StringLatin1.hashCode(value)
            : StringUTF16.hashCode(value);
    }
    return h;
}

只是轉發到StringLatin1StringUTF16 ,讓我們看看他們有什么:

// StringLatin1
public static int hashCode(byte[] value) {
    int h = 0;
    for (byte v : value) {
        h = 31 * h + (v & 0xff);
    }
    return h;
}

// StringUTF16
public static int hashCode(byte[] value) {
    int h = 0;
    int length = value.length >> 1;
    for (int i = 0; i < length; i++) {
        h = 31 * h + getChar(value, i);
    }
    return h;
}

如您所見,它們都只是根據字符串中的各個字符進行一些簡單的數學運算。 所以它完全是基於內容的,因此顯然總是會為相同的字符產生相同的結果。

暫無
暫無

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

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