在 Java 集合框架中,HashMap、HashSet 等依賴雜湊表儲存資料。JVM 先比較 hashCode() 定位桶位,再呼叫 equals() 確認物件相等。若違反契約,會導致集合行為異常。
// Object.equals():比較記憶體位址
public boolean equals(Object obj) {
return (this == obj);
}
// Object.hashCode():native 方法,通常基於記憶體位址
public native int hashCode();
String、Integer 等包裝類別已覆寫 equals() 比較內容值,hashCode() 也依內容計算(如 String 使用 31 為基數的多項式雜湊)。
a.equals(b) == true,則 a.hashCode() == b.hashCode() 必須成立。equals() 回傳 false 時,hashCode() 可以相同(雜湊碰撞)。hashCode() 應回傳相同值(前提是 equals 比較欄位未變)。class Broken {
@Override public boolean equals(Object o) { return true; }
@Override public int hashCode() { return new Random().nextInt(); } // 每次不同
}
Set<Broken> set = new HashSet<>();
set.add(new Broken());
set.add(new Broken());
System.out.println(set.size()); // 可能輸出 2,甚至更多,違反 Set 不重複特性
class A {
@Override public boolean equals(Object obj) {
System.out.println("呼叫 equals");
return false; // 永遠不相等
}
@Override public int hashCode() {
System.out.println("呼叫 hashCode");
return 1; // 固定雜湊碼
}
}
public class Test {
public static void main(String[] args) {
Map<A, Object> map = new HashMap<>();
map.put(new A(), new Object()); // 呼叫 hashCode
map.put(new A(), new Object()); // 呼叫 hashCode → 發現碰撞 → 呼叫 equals
System.out.println("Map size: " + map.size()); // 輸出 2
}
}
輸出分析:
第一次 put 僅呼叫 hashCode();
第二次 put 發現相同雜湊碼,進而呼叫 equals() 比較,因回傳 false 視為不同鍵值,故 size 為 2。
x.equals(x) == truex.equals(y) == y.equals(x)x.equals(y) && y.equals(z) → x.equals(z)x.equals(null) == falseequals() 與 hashCode()(基於相同欄位)。hashCode() 提升效能。@Value (Lombok) 或 record (Java 16+)。