Java 集合分兩大派:
Set 如何高效去重?遍歷調用 equals() 成本 O(n)。hashCode() 提供「分桶」機制:
hashCode() 計算哈希值index = (hash & 0x7FFFFFFF) % table.lengthequals() 比較大幅減少 equals() 調用次數,提升插入/查找性能至接近 O(1)。
| 策略 | 優點 | 缺點 |
|---|---|---|
| 所有屬性參與 | 分佈均勻,衝突極低 | 計算開銷大,影響性能 |
| 少數關鍵屬性 | 計算快 | 衝突率高,退化為鏈表查找 |
工程妥協: 緩存哈希值,屬性變更時重新計算(如 String、BigInteger)。
int index = (hash & 0x7FFFFFFF) % tab.length;
& 0x7FFFFFFF:確保正數(哈希值可能為負)key 保存原對象,equals() 精確判斷同一性x.equals(x) == truex.equals(y) == y.equals(x)x.equals(y) && y.equals(z) → x.equals(z)x.equals(null) == falsex.equals(y) == true → x.hashCode() == y.hashCode()x.equals(y) == false → hashCode() 可能相等也可能不等
hashCode():不等 → 判定不等,結束equals():不等 → 判定不等;相等 → 判定相等public class Person {
private int age;
private int sex; // 0男 1女
private String name;
private final int PRIME = 37;
@Override
public int hashCode() {
int result = 1;
result = (result + Integer.hashCode(age) + Integer.hashCode(sex)) * PRIME;
result = PRIME * result + (name == null ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person other = (Person) obj;
return age == other.age && sex == other.sex
&& Objects.equals(name, other.name);
}
}
Setset = new HashSet<>(); set.add(new Person(11, 1, "張三")); // p1 set.add(new Person(12, 1, "李四")); // p2 set.add(new Person(11, 1, "張三")); // p3 同 p1 set.add(new Person(11, 1, "李四")); // p4 // 結果:set.size() == 3(p1/p3 視為同一元素)