紅黑樹查找複雜度 O(log N)。哈希表透過**哈希函數**將鍵映射為整數索引,實現**平均 O(1)** 查找。核心挑戰:
a.equals(b) ⇒ hash(a) == hash(b)!a.equals(b) ⇒ hash(a) != hash(b)(理想情況,實際追求低衝突率)public int hashCode() { return value; }
public int hashCode() {
long bits = Double.doubleToLongBits(value);
return (int)(bits ^ (bits >>> 32));
}
public int hashCode() {
int h = 0;
for (char c : value) h = 31 * h + c;
return h;
}
31 的選擇:質數、接近 2 的冪(31*i = (i<<5) - i 編譯器優化)、衝突率低。但 Horner 法易構造衝突(如 "Aa" 與 "BB"),曾導致 HashMap DoS 漏洞(CVE-2012-2739)。
public class Student {
private int number;
private String name;
private String className;
@Override
public int hashCode() {
int hash = 17; // 非零質數種子
hash = 31 * hash + (name == null ? 0 : name.hashCode());
hash = 31 * hash + (className == null ? 0 : className.hashCode());
hash = 31 * hash + number;
return hash;
}
// 必須重寫 equals 保持一致性
}
| 寫法 | 代碼 | 問題 |
|---|---|---|
| 直接取模 | key.hashCode() % M | 負數餘數為負,索引越界 |
| Math.abs 取模 | Math.abs(key.hashCode()) % M | hashCode == Integer.MIN_VALUE (-0x80000000) 時 Math.abs 仍為負(溢位) |
| 位運算取模(正確) | (key.hashCode() & 0x7fffffff) % M | 清除符號位,保證非負,無溢位風險 |
原理:0x7fffffff = 2^31 - 1,保留低 31 位,等效於對正數取模,負數變正數且分布均勻。
標準 hashCode 針對一般用途設計。高併發、大數據、安全敏感場景建議使用:
hashCode() 基於記憶體位址,自訂類別**必須**重寫 hashCode 與 equals。(h & 0x7fffffff) % M** 避免負數與 MIN_VALUE 陷阱。