HashMap 是 Java 開發者每天都在用的集合,但其底層實現細節往往被忽視。本文通過源碼級分析,揭示 HashMap 的存取流程、哈希衝突解決、擴容觸發條件,並解答「為什麼推薦用 String 做 Key」等高頻疑問。
transient Entry<K,V>[] table:核心存儲數組,長度始終為 2 的冪次。Entry 節點包含 hash、key、value、next 指針,形成單向鏈表。table 初為空數組 EMPTY_TABLE。table 擴容為 16。hash(key) → indexFor(hash, table.length)(位運算 h & (length-1))。hash 相等且 key.equals() 則覆蓋值;否則頭插法新增節點。size >= threshold 且當前索引位置非空 → resize(2 * table.length),重新計算所有節點索引並遷移。示例演示:依次 put "one"~"seven",索引分佈與鏈表形成過程圖解。
table[index] 遍歷鏈表/紅黑樹。hash 相等且 key.equals() 即命中,返回 value。null key 特殊處理:hash 固定為 0,存放於 table[0],getForNullKey 遍歷查找。
鏈表/紅黑樹節點儲存完整 key,遍歷時通過 equals() 精確比對,確保取到正確 Value。
put 過程中檢測到 key 相等(hash 相等且 equals 為 true),直接覆蓋舊值並返回舊值,保證 Key 唯一性。
private int hash),多次調用極快。可以,但必須保證參與 hashCode 計算的字段不變。示例:Key 類基於 name 計算 hash,修改 address 不影響查找;若修改 name 則導致 get 返回 null。
| 參數 | 默認值 | 建議 |
|---|---|---|
| initialCapacity | 16 | 預估數據量大時預設較大值,減少擴容次數 |
| loadFactor | 0.75 | 空間換時間;極少擴容場景可調大至 0.9 |