首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java equals 與 hashCode 契約深度解析:原始碼層面的完整剖析

     編輯:Java 相關知識

核心契約:equals 與 hashCode 的不變約定

在 Java 集合框架中,equals() 與 hashCode() 遵循嚴格契約:

若兩個物件根據 equals() 判定相等,則其 hashCode() 必須相同。
反之不成立:hashCode 相同不代表 equals 為 true(哈希碰撞)。

Object 類別預設實作

public boolean equals(Object obj) {
    return (this == obj);  // 記憶體位址比較
}

public native int hashCode();  // 通常基於記憶體位址生成

equals 必須滿足的五大特性

特性定義程式碼驗證
自反性x.equals(x) == true任何非 null 物件等於自己
對稱性x.equals(y) → y.equals(x)雙向一致
傳遞性x.equals(y) ∧ y.equals(z) → x.equals(z)鏈式傳遞
一致性多次調用結果不變前提:比較欄位未修改
非空性x.equals(null) == false防禦性程式設計

String 類別的覆寫實作

equals 內容比較邏輯

public boolean equals(Object anObject) {
    if (this == anObject) return true;           // 同一參考快速返回
    if (anObject instanceof String) {            // 型別檢查
        String another = (String) anObject;
        if (value.length != another.value.length) return false; // 長度不同直接 false
        for (int i = 0; i < value.length; i++) {
            if (value[i] != another.value[i]) return false;     // 逐字元比較
        }
        return true;
    }
    return false;
}

hashCode 多項式滾動雜湊

public int hashCode() {
    int h = hash;  // 快取欄位
    if (h == 0 && value.length > 0) {
        for (char c : value) {
            h = 31 * h + c;  // 31 為質數,減少碰撞
        }
        hash = h;
    }
    return h;
}

數學公式:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

實戰覆寫指南

標準覆寫模板

public class Employee {
    private String id;
    private String name;
    private Department dept;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee that = (Employee) o;
        return Objects.equals(id, that.id) &&
               Objects.equals(name, that.name) &&
               Objects.equals(dept, that.dept);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, dept);  // Java 7+ 建議用法
    }
}

常見錯誤與最佳實踐

❌ 錯誤示範

  • 只覆寫 equals 不覆寫 hashCode → HashMap/HashSet 失效
  • equals 使用 instanceof 而非 getClass() → 破壞對稱性(子類別問題)
  • hashCode 計算欄位與 equals 不一致 → 違反契約
  • 使用可變欄位計算 hashCode → 物件放入集合後修改導致找不到

✅ 最佳實踐

  • 使用 Objects.equals() 與 Objects.hash() 簡化程式碼(Java 7+)
  • 僅使用不可變欄位參與比較與雜湊計算
  • 考慮效能:先比較廉價欄位(int、boolean),再比較昂貴欄位(String、集合)
  • ID 欄位若為唯一識別碼,可僅比較 ID 大幅提升效能
  • 單元測試驗證契約:EqualsVerifier 庫自動化測試

集合框架中的實際影響

集合類型依賴方法違約後果
HashMap / HashSethashCode → equals重複元素、查找失敗、記憶體洩漏
TreeMap / TreeSetcompareTo / Comparator不受 hashCode 影響,但需一致性
IdentityHashMap== (記憶體位址)不使用 equals/hashCode

效能優化技巧

// 快取 hashCode(適用於不可變物件)
private volatile int hashCache = 0;

@Override
public int hashCode() {
    int h = hashCache;
    if (h == 0) {
        h = computeHashCode();  // 實際計算
        hashCache = h;
    }
    return h;
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved