在 Java 開發中,hashCode() 和 equals() 是 Object 類中兩個最基礎卻又最容易被忽視的方法。既然 Object 是所有類的父類,這意味著每個 Java 物件都擁有這兩個方法。它們在集合框架(特別是 HashMap、HashSet 等基於哈希表的容器)中扮演著關鍵角色:決定物件的唯一性與儲存位置。
若不正確重寫這兩個方法,可能導致邏輯錯誤、效能低落,甚至資料不一致。本文將從原理、規範、實戰案例三個維度,帶你徹底掌握它們的核心機制。
在 Object 類中,equals() 的實現極其簡單:
public boolean equals(Object obj) {
return (this == obj);
}
這代表預設比較的是「記憶體位址」(即引用是否指向同一個物件)。但許多核心類(如 String、Integer、Date 等)已重寫此方法,改為比較「內容值」。
hashCode() 是一個 native 方法,其預設實現通常基於物件的記憶體位址轉換而來。以 String 為例,其演算法為:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
關鍵點:hashCode 返回的不一定是真實的記憶體位址,而是一個經過演算法計算的整數「指紋」。
當向 HashMap 或 HashSet 新增元素時:
hashCode() 計算雜湊值,定位到桶位(bucket)。equals() 逐一比對,確認是否真正「相同」。這機制極大降低了比較次數,從 O(n) 降至接近 O(1)。
Java 語言規範(JLS)強制要求:
a.equals(b) == true,則 a.hashCode() == b.hashCode() 必須成立。a.equals(b) == false,hashCode() 可以相同(哈希衝突),也可以不同。違反規則一雖不會編譯報錯,但會導致基於哈希的集合失效(如 HashSet 存入重複物件、HashMap 找不到鍵值)。
| 條件 | hashCode 必相等? | equals 必相等? |
|---|---|---|
| equals 為 true | 是 | 是 |
| equals 為 false | 不一定 | 否 |
| hashCode 相等 | — | 不一定(衝突) |
| hashCode 不等 | — | 一定不等 |
class A {
@Override
public boolean equals(Object obj) {
System.out.println("判斷 equals");
return false; // 永遠不相等
}
@Override
public int hashCode() {
System.out.println("判斷 hashCode");
return 1; // 永遠返回 1,製造衝突
}
}
public class Test {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put(new A(), new Object());
map.put(new A(), new Object());
System.out.println(map.size()); // 輸出 2
}
}
輸出分析:
判斷 hashCode // 第 1 次 put
判斷 hashCode // 第 2 次 put,發現 hash 相同
判斷 equals // 必須進一步比對
2
雖然 hashCode 相同,但 equals 返回 false,因此視為不同鍵,size() 為 2。這證實了「先比 hash,再比 equals」的流程。
除了與 hashCode 的契約,equals() 本身必須滿足:
x.equals(y) 為 true → y.equals(x) 也為 true。x.equals(x) 必為 true。x.equals(y) 且 y.equals(z) 為 true → x.equals(z) 為 true。x.equals(null) 永遠返回 false;與不同類型物件比較也返回 false。違反任一原則,都可能在集合操作中產生不可預期的 Bug。
equals() 時,務必覆寫 hashCode()。Objects.hash()、Objects.equals() 簡化代碼。hashCode 提升效能(如 String)。hashCode() 與 equals() 是 Java 物件身份識別的基石。理解其底層邏輯、嚴守規範契約、並在實戰中正確實現,是寫出健壯、高效 Java 代碼的必修課。下次當你定義一個會放入 HashSet 或作為 HashMap 鍵的類時,請務必花幾分鐘正確實現這兩個方法——這將為你省去無數調試的夜晚。