『Java 語法基礎』對 equals() 和 hashCode() 的理解

BNTang發表於2024-03-18

為什麼在重寫 equals 方法的時候需要重寫 hashCode 方法

因為有強制的規範指定需要同時重寫 hashcode 與 equals 方法,許多容器類,如 HashMap、HashSet 都依賴於 hashcode 與 equals 的規定。

在Java中,equals()hashCode() 方法是 Object 類的兩個非常重要的方法,它們用於比較物件的相等性以及計算物件的雜湊碼。

equals() 方法用於判斷兩個物件是否邏輯上相等,也就是兩個物件的內容是否相同。它通常需要根據實際情況在子類中重寫,以提供正確的相等性邏輯。預設情況下(如 Object 類中的實現),equals() 方法比較物件的記憶體地址,也就是說,只有同一個物件才被認為是相等的。

hashCode() 方法則用於計算物件的雜湊碼,它被用於雜湊資料結構,如 HashSetHashMap 等。在雜湊資料結構中,物件的雜湊碼通常被用來決定物件應該存放的位置。

根據 Java 規範,有關 equals()hashCode() 的重要契約包括:

  1. 如果兩個物件相等,那麼它們的 hashCode() 方法必須返回相同的整數。
  2. 如果兩個物件的 hashCode() 方法返回相同的整數,這並不要求它們一定相等,但雜湊表的效率可能受到影響。

之所以在重寫 equals() 方法時必須同時重寫 hashCode() 方法,是為了維持這兩個方法之間的一致性。如果兩個物件是相等的,但它們的雜湊碼不相同,這將違反 hashCode() 方法的契約,可能導致無法正確地存取雜湊資料結構中的物件。例如,在使用 HashMap 時,如果兩個鍵物件相等,它們必須具有相同的雜湊碼,否則可能會導致其中一個物件無法被正確地找到。

有沒有可能兩個不相等的物件有相同的 hashcode?

有可能,即使兩個物件不相同,它們也可能具有相同的 hashCode 值,這稱為雜湊衝突。雜湊衝突是雜湊演算法的一個固有屬性,因為雜湊碼是將物件資訊對映到一個有限的整數域上。由於這個整數域範圍的限制,相對於可能的物件狀態來說是不足夠的,從而導致不同物件間的雜湊值可能相同。

Java語言規範中關於 hashCode 的合同確實指出,如果兩個物件相同,則它們的雜湊碼一定相同,但並未規定不同的物件必須產生不同的雜湊碼。因此,在基於雜湊的資料結構中,如 HashMap,便可能出現雜湊碰撞。在這種情況下,HashMap 會使用 連結串列紅黑樹 來管理相同雜湊碼的不同鍵,確保了即便發生了雜湊碰撞,也能透過 equals() 方法準確地區分和處理這些鍵。

因此,為了確保諸如 HashMapHashSet 這樣的基於雜湊的集合能夠正確且高效地運作,合理地實現 hashCodeequals 方法以及妥善處理雜湊衝突是至關重要的。

兩個相同的物件會有不同的 hash code 嗎?

不能,根據 hash code 的規定,這是不可能的。

在 Java 中,當你重寫一個類的 equals() 方法時,根據 Java 的規範,你也必須重寫 hashCode() 方法,以保證同一個類的兩個相等的物件(即 equals() 方法返回 true)返回相同的 hash code 值。這是因為 Java 中的集合類,如 HashMapHashSet,依賴於 hashCode()equals() 方法來確定物件的唯一性。

如果兩個物件根據 equals(Object) 方法是相等的,那麼它們的 hashCode()方 法也必須返回相同的整數值。如果沒有重寫 hashCode(),那麼預設的 Object 類的實現將產生基於物件記憶體地址的 hash code,這可能會違反這一規定,並且在使用雜湊表的過程中導致效能下降。所以重寫 equals() 時一定要同步重寫 hashCode(),以確保行為的一致性。

相關文章