若兩個對象根據equals()判斷相等,則它們的hashCode()必須相等。
反之,hashCode()相等的對象,equals()不一定相等(哈希碰撞)。
class Student {
private int stuNo;
private String name;
// 建構子、getter 省略
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof Student) {
return this.stuNo == ((Student) obj).stuNo;
}
return false;
}
// 忘記重寫 hashCode()!
}
public class Test {
public static void main(String[] args) {
Set<Student> set = new HashSet<>();
set.add(new Student(123, "Tom"));
set.add(new Student(456, "Jerry"));
set.add(new Student(123, "Lily")); // 同學號,應去重
for (Student s : set) {
System.out.println(s.getStuNo() + " - " + s.getName());
}
}
}
123 - Lily
456 - Jerry
123 - Tom // 重複學號未去重!
HashSet.add() 內部調用 HashMap.put()put() 計算 hash(key) 確定桶位置e.hash == hash && (e.key == key || key.equals(k))hashCode,不等則直接判定為不同對象,不再調用 equals未重寫 hashCode 時,預設使用 Object.hashCode()(基於內存地址),導致邏輯相等的對象哈希值不同,落入不同桶位,equals 根本不被調用。
@Override
public int hashCode() {
return Integer.hashCode(stuNo); // 或 Objects.hash(stuNo)
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Student)) return false;
Student other = (Student) obj;
return this.stuNo == other.stuNo;
}
456 - Jerry
123 - Tom // 正確去重,保留首個
性能優化: equals 可能涉及複雜業務邏輯(多字段比較、遞歸比較),hashCode 比較極快(整數比較)。先過濾掉明顯不同的對象,大幅提升查找效率。
equals 時必須重寫 hashCodeObjects.hash(field1, field2...) (Java 7+) 或 IDE 自動生成equals/hashCode 的字段應為 final 或不可變| 誤區 | 正確理解 |
|---|---|
| hashCode 相等即 equals 相等 | 哈希碰撞不可避免,必須再調用 equals 確認 |
| 只要不放入 HashSet/HashMap 就不用重寫 | 未來代碼變更可能放入,最好養成習慣 |
| 返回常數如 42 即可 | 雖合法但退化為鏈表,性能極差 |
| 用內存地址計算 hashCode | 違反契約,邏輯相等對象會有不同哈希 |