哈希容器(HashMap、HashSet、Hashtable)依賴 hashCode() 快速定位桶位置,再用 equals() 精確比較。若契約被破壞,將導致:
x.equals(x) == truex.equals(y) == y.equals(x)x.equals(y) && y.equals(z) ⇒ x.equals(z)x.equals(null) == falsepublic class Person {
private Long id;
private String name;
private LocalDate birthday;
@Override
public boolean equals(Object o) {
if (this == o) return true; // 1. 同引用快速返回
if (o == null || getClass() != o.getClass()) return false; // 2. null/類型檢查
Person person = (Person) o;
return Objects.equals(id, person.id) && // 3. 逐字段比較
Objects.equals(name, person.name) &&
Objects.equals(birthday, person.birthday);
}
@Override
public int hashCode() {
return Objects.hash(id, name, birthday); // JDK 7+ 推薦
// 手寫版:31 * (31 * id.hashCode() + name.hashCode()) + birthday.hashCode()
}
}
// put 流程簡化
int hash = hash(key.hashCode()); // 擾動函數
int index = (n - 1) & hash; // 定位桶
Node e = table[index];
if (e == null) {
newNode(hash, key, value, null); // 直接插入
} else {
// 遍歷鏈表/樹
for (Node cur = e; cur != null; cur = cur.next) {
if (cur.hash == hash && (cur.key == key || key.equals(cur.key))) {
// 找到重複 key:hash 相等 AND equals 相等
V old = cur.value;
cur.value = value;
return old;
}
}
// 未找到,鏈表尾部新增
}
// get 流程
Node e = table[index];
for (Node cur = e; cur != null; cur = cur.next) {
if (cur.hash == hash && (cur.key == key || key.equals(cur.key))) {
return cur.value; // 必須 equals 相等才能命中
}
}
return null;
關鍵洞察:hashCode() 決定搜索範圍(哪個桶),equals() 決定精確匹配。hashCode 不等直接跳過 equals,提升性能。
| 錯誤代碼 | 違反契約 | 後果 |
|---|---|---|
| 只重寫 equals,不重寫 hashCode | equals 相等但 hashCode 不等 | Set 存入重複元素,Map 找不到已存 Key |
| hashCode 依賴可變字段 | 對象修改後 hashCode 改變 | Map/Set 中元素「丟失」,無法 remove |
| equals 未檢查 getClass,用 instanceof | 破壞對稱性(子類重寫) | Set 行為異常,違反集合規範 |
| equals 比較非核心業務字段 | 業務邏輯不一致 | 去重/查找語義錯誤 |
// String 核心實現片段
public final class String {
private final char value[]; // JDK 8 為 char[], JDK 9+ 為 byte[] + coder
private int hash; // 默認 0 表示未計算
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
hash = h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
}
return h;
}
public boolean equals(Object anObject) {
if (this == anObject) return true;
if (anObject instanceof String aString) { // 模式匹配
// 比較 coder + value 數組內容
}
return false;
}
}
優勢:不可變 ⇒ hashCode 緩存安全、線程安全、可作 Map Key/Set 元素、字符串常量池複用。
getClass() 而非 instanceof