重新實現hashCode()方法

2023-04-23 06:00:49

在Java中,為了讓物件在集合中能夠更高效地進行查詢和比較,我們通常需要重寫物件的equals()hashCode()方法。其中,equals()方法用於比較兩個物件是否相等,而hashCode()方法則用於返回物件雜湊值,供集合類使用。

預設情況下,Java會根據每個物件的記憶體地址來計算雜湊值,因此如果兩個物件在記憶體中的位置不同,它們的雜湊值也會不同。但是,在實際開發中,我們可能需要比較的是物件的屬性值而不是記憶體地址,這時就需要自己來實現hashCode()方法了。

為什麼需要重新實現hashCode()方法

雖然預設實現的hashCode()方法可以滿足基本的雜湊表需求,但是它有一個很大的問題:它只是返回物件的記憶體地址的雜湊碼,這意味著兩個內容完全相同的物件在雜湊表中還是會被認為是不同的物件,這樣就會浪費大量的空間和時間。例如:

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.hashCode()); // 輸出 99162322
System.out.println(s2.hashCode()); // 輸出 99162322

雖然s1s2的內容相同,但是它們在記憶體中的地址不同,因此它們的雜湊值也不同。

在實際使用中,這可能會導致一些問題,比如無法正確識別集合中的重複元素。

但是一些標準Java類庫中的類(例如StringInteger等)已經重寫了hashCode()方法,以便讓具有相同屬性值的物件具有相同的雜湊碼。

所以上面程式碼結果會顯示雜湊值相同。

如何重新實現hashCode()方法

要重新實現hashCode()方法,我們需要結合物件的屬性值來計算雜湊碼,以便讓具有相同屬性值的物件具有相同的雜湊碼。一般來說,可以採用以下步驟:

  1. 把物件的非零屬性用一個質數(比如31)進行加權,並把它們相加。
  2. 如果屬性是布林型,則使用(f ? 1 : 0) 的形式轉換成數值型。
  3. 如果屬性是浮點型,則使用Float.floatToIntBits(f)的方式把它們轉換成整型。
  4. 如果屬性是雙精度型,則使用Double.doubleToLongBits(f)的方式把它們轉換成長整型,並對其進行互斥或操作。
  5. 如果屬性是陣列,則對每個元素進行遞迴處理。

例如,在一個自定義的Person類中,如果我們想讓兩個物件在nameage屬性都相同的情況下返回相同的雜湊碼,可以按照以下方式重新實現hashCode()方法:

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + name.hashCode();
    result = 31 * result + age;
    return result;
}

其中,1731都是選定的質數。

 

 

注意事項

在重新實現hashCode()方法時,需要牢記以下幾點:

  1. 雜湊碼的計算方式應該儘量均勻分佈,這樣可以提高雜湊表的效能。
  2. 如果兩個物件的equals()方法返回true,那麼它們的雜湊碼應該相同。
  3. 如果物件的屬性值發生變化,那麼它的雜湊碼也應該隨之變化。
  4. 雜湊碼的計算過程中,應該避免使用可能會發生溢位的操作。
  5. 建議使用自動生成的hashCode()方法,例如Eclipse和IntelliJ IDEA都支援自動生成hashCode()equals()方法的功能。

總結

重新實現hashCode()方法可以提高雜湊表的效率,使得具有相同屬性值的物件具有相同的雜湊碼。要實現hashCode()方法,需要按照一定的步驟進行計算,並考慮到一些細節問題。在實際開發中,建議使用自動生成的hashCode()方法。