Object.equals(Object) 定義物件「邏輯相等」語義,必須滿足 等價關係 五大契約:
x.equals(x) == truex.equals(y) == y.equals(x)x.equals(y) && y.equals(z) → x.equals(z)x.equals(null) == false@Override
public boolean equals(Object obj) {
if (this == obj) return true; // 同一引用
if (obj == null) return false; // 非空性
if (getClass() != obj.getClass()) return false; // 嚴格類型檢查
Person other = (Person) obj;
return Objects.equals(name, other.name) && age == other.age;
}
@Override
public int hashCode() {
return Objects.hash(name, age); // 內部用 31 為質數乘法組合
}
// 或手寫(避免創建數組/自動裝箱)
@Override
public int hashCode() {
int h = name == null ? 0 : name.hashCode();
h = 31 * h + age;
return h;
}
Setset = new HashSet<>(); set.add(new Person("Alice", 20)); set.add(new Person("Alice", 20)); // 邏輯相同 // 若只覆寫 equals(),不覆寫 hashCode(): // → 兩物件 hashCode 不同 → 落入不同桶位 → set.size() == 2(錯誤!) // 正確:同時覆寫 → 同桶位 → equals 判定相等 → set.size() == 1
原理:散列容器先比 hashCode 定桶位,再遍歷桶內鏈表/紅黑樹呼叫 equals。若 hashCode 不同,equals 根本不會被調用。
equals 判斷相等,hashCode 可不覆寫(繼承 Object 的身份雜湊碼)。| 錯誤 | 後果 | 修正 |
|---|---|---|
| equals 用 instanceof 檢查類型 | 破壞對稱性(子類 vs 父類) | 用 getClass() == obj.getClass() 或 Objects.equals(getClass(), obj.getClass()) |
| equals 比較可變欄位 | 放入 Set/Map 後修改欄位 → 找不到/重複 | 僅用不可變欄位計算 equals/hashCode |
| hashCode 只用單一欄位 | 碰撞率極高,退化成鏈表 O(n) | 組合多個關鍵欄位,質數乘法 |
| Arrays.equals 比較陣列欄位 | 忘記處理 null、長度不同 | 用 Arrays.equals(a, b) 或 Objects.deepEquals |
Generate → equals() and hashCode() 可勾選欄位自動生成標準實作。@EqualsAndHashCode(可指定 of、exclude、callSuper)。equals 相等,hashCode 必同;
進散列容器,雙覆寫硬通。
不變欄位算,質數乘法工;
instanceof 別用,getClass 正統。