JavaRush /Java Blog /Random-TW /喝咖啡休息#168。為什麼要重寫 Java 中的 equals 和 hashcode 方法?

喝咖啡休息#168。為什麼要重寫 Java 中的 equals 和 hashcode 方法?

在 Random-TW 群組發布

為什麼要重寫 Java 中的 equals 和 hashcode 方法?

資料來源: Medium 本文重點介紹兩個密切相關的方法:equals()hashcode()。您將了解它們如何相互交互以及如何正確覆蓋它們。 喝咖啡休息#168。 為什麼要重寫 Java 中的 equals 和 hashcode 方法? - 1

為什麼我們要重寫 equals() 方法?

在 Java 中,我們不能重載==+=-+等運算子的行為。他們按照給定的流程工作。例如,考慮==運算子的操作。

== 運算子如何運作?

它檢查比較的兩個引用是否指向記憶體中的相同實例。只有當兩個引用表示記憶體中的相同實例時,==運算子才會計算為 true。我們來看看範例程式碼:
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
      }
假設在您的程式中,您在不同的位置建立了兩個Person 物件並想要比較它們。
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
從商業角度來看,兩者看起來是一樣的,對吧?但對於 JVM 來說它們並不相同。由於它們都是使用new關鍵字建立的,因此這些實例位於不同的記憶體段中。因此==運算子將會傳回false。但是如果我們不能重寫==運算符,那麼我們要如何告訴 JVM 我們希望這兩個物件得到相同的對待?這就是.equals()方法發揮作用的地方。您可以重寫equals()來檢查某些物件的某些欄位是否具有相同的值,以便認為它們相等。您可以選擇要比較的欄位。如果我們說兩個Person物件只有具有相同的年齡和相同的名稱是相同的,那麼 IDE 將產生類似這樣的內容 來自動建立equals() :
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
讓我們回到之前的例子。
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
是的,我們不能重載==運算子來按照我們想要的方式比較對象,但是 Java 為我們提供了另一種方式 - equals ()方法,我們可以根據需要重寫該方法。 請記住,如果我們不在類別中提供自訂版本的.equals()(也稱為覆蓋),則Object類別中預先定義的.equals()==運算子的行為將相同。 預設的equals()方法繼承自Object,將檢查正在比較的兩個實例在記憶體中是否相同!

為什麼我們要重寫 hashCode() 方法?

Java 中的某些資料結構(例如HashSetHashMap)是基於應用於這些元素的雜湊函數來儲存其元素。雜湊函數是hashCode()。如果我們可以選擇重寫.equals()方法,那麼我們也應該可以選擇重寫hashCode()方法。這是有原因的。畢竟,繼承自Object的 hashCode() 的預設實作認為記憶體中的所有物件都是唯一的!但讓我們回到這些雜湊資料結構。這些資料結構有一個規則。 HashSet不能包含重複的值,HashMap不能包含重複的鍵。HashSet是使用HashMap實現的,每個HashSet值都作為鍵儲存在HashMap中。HashMap是如何運作的?HashMap是一個具有多個段的原生數組。每個段都有一個鍊錶(linkedList)。這個鍊錶儲存了我們的密鑰。HashMap使用hashCode()方法為每個鍵找到正確的 linkedList ,然後迭代該linkedList的所有元素,並對每個元素應用equals()方法以檢查該元素是否包含在其中。不允許有重複的密鑰。當我們將某些內容放入HashMap時,鍵就會儲存在這些鍊錶之一中。該鍵將儲存在哪個鍊錶中由該鍵的hashCode()方法的結果顯示。也就是說,如果key1.hashCode()結果為 4,則key1將會儲存在現有LinkedList 陣列的第 4 段。預設情況下,hashCode()方法為每個實例傳回不同的結果。如果我們有一個預設的equals(),其行為類似於==,將記憶體中的所有實例視為不同的對象,那麼就不會有問題。您可能還記得,在前面的範例中,我們說過,如果Person實例的年齡和名稱相同,則 我們希望它們被視為相等。 喝咖啡休息#168。 為什麼要重寫 Java 中的 equals 和 hashcode 方法? - 2
Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!
現在讓我們建立一個映射來將這些實例儲存為鍵,並以特定字串作為值對。
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
Person類別中,我們沒有重寫hashCode方法,但是我們有一個重寫的equals方法。由於預設hashCode對於person1.hashCode()person2.hashCode()的不同 Java 實例給出不同的結果,因此很有可能會得到不同的結果。我們的地圖可以以不同的連結清單中的不同的人結束。 這違背了HashMap 的喝咖啡休息#168。 為什麼要重寫 Java 中的 equals 和 hashcode 方法? - 3邏輯。畢竟,HashMap不能有多個相同的按鍵! 重點是繼承自Object類別的預設hashCode()是不夠的。即使我們重寫了Person類別的equals()方法。這就是為什麼我們在重寫equals方法之後必須重寫hashCode()方法。現在讓我們解決這個問題。我們需要重寫hashCode()方法,以便它考慮與equals()相同的字段,即agename
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
@Override
public int hashCode() {
        int prime = 31;
        return prime*Objects.hash(name, age);
    }
hashCode()方法中,我們使用了一個簡單的值(您可以使用任何其他值)。不過,建議使用質數來減少問題。讓我們再次嘗試將這些鍵儲存在HashMap中:
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode()person2.hashCode()是相同的。假設它們是 0。HashMap 轉到段 0,LinkedList在其中將person1保存為值為「1」的鍵。第二種情況,當HashMap再次到桶0儲存key為「2」的person2時,會發現那裡已經存在另一個與它相等的key。這樣它將覆蓋以前的密鑰。而只有關鍵的person2才會存在於我們的HashMap中。 這就是我們了解HashMap規則的工作原理的方式,該規則規定不能使用多個相同的鍵! 但是,請記住,不相等的實例可以具有相同的雜湊碼,並且相等的實例必須傳回相同的雜湊碼。喝咖啡休息#168。 為什麼要重寫 Java 中的 equals 和 hashcode 方法? - 4
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION