當對象作為集合元素(HashSet)、映射鍵(HashMap、Hashtable)或參與基於哈希的去重/查找操作時,必須同時重寫 equals() 和 hashCode()。違反契約將導致:
contains、remove 失效)
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // 自反性
if (obj == null || getClass() != obj.getClass()) return null; // 類型檢查
MyClass other = (MyClass) obj;
return Objects.equals(field1, other.field1)
&& Objects.equals(field2, other.field2)
// ... 比較所有業務關鍵字段
}
| 公理 | 要求 | 違反後果 |
|------|------|----------|
| **自反性** | x.equals(x) == true | 自身不等,集合邏輯崩潰 |
| **對稱性** | x.equals(y) == y.equals(x) | 不同集合操作結果不一致 |
| **傳遞性** | x.equals(y) && y.equals(z) => x.equals(z) | 分組/去重邏輯錯誤 |
| **一致性** | 多次調用結果不變(字段不變前提) | 集合行為不可預測 |
| **非空性** | x.equals(null) == false | NPE 風險 |
Objects.equals():空安全,簡潔getClass() 保證對稱性;instanceof 允許子類比較但難保對稱
@Override
public int hashCode() {
return Objects.hash(field1, field2, field3); // JDK 7+ 推薦
// 或手動實現(性能敏感場景):
// int result = 17;
// result = 31 * result + (field1 == null ? 0 : field1.hashCode());
// result = 31 * result + (field2 == null ? 0 : field2.hashCode());
// return result;
}
31 * i == (i << 5) - i,JVM 可優化為位移+減法key.hashCode()index = (n - 1) & hash 定位桶位(n 為 2 的冪)equals() 比較| 違反類型 | 現象 | 根因 |
|---|---|---|
| equals 相等但 hashCode 不等 | map.get(key) 返回 null;set.contains(obj) 返回 false | 對象落入不同桶,equals 從未被調用 |
| equals 不等但 hashCode 相等 | 性能退化(碰撞增加),但邏輯正確 | 同桶內通過 equals 正確區分 |
| 可變字段參與 hashCode | 對象修改後無法在 Map 中找到 | 哈希值變化導致桶位錯位 |
HashMap KeyHashSet/LinkedHashSetArrayList/LinkedList(靠索引/迭代器)TreeMap/TreeSet(靠 Comparable/Comparator)Object 實現(身份相等)滿足需求Alt+Insert → equals/hashCode@EqualsAndHashCode(可指定字段、調用父類)EqualsVerifier 庫自動檢測契約合規性鐵律:參與 equals/hashCode 的字段必須不可變(或至少在對象作為 Key/Element 期間不可變)。
// 危險示例
class MutableKey {
private String name;
public void setName(String name) { this.name = name; } // 修改後 hashCode 改變
// equals/hashCode 基於 name
}
Map map = new HashMap<>();
MutableKey key = new MutableKey("A");
map.put(key, "value");
key.setName("B"); // 災難:key 現在在錯誤的桶中,永遠找不回
map.get(key); // null
final 字段、無 setter、構造器注入)equals() 定義業務邏輯上的相等,hashCode() 提供高性能分桶索引。二者契約是 Java 集合框架正確性的基石。掌握:
將「重寫 equals 必重寫 hashCode」內化為肌肉記憶,是每位 Java 開發者的專業底線。