HashMap 基於 哈希表 實現,核心操作:
put(key, value):計算 key.hashCode() → 定位桶位置 → 儲存 Entry<K,V> (含 key、value、hash、next)get(key):同樣計算 hash 定位桶 → 遍歷鏈表/樹 → 呼叫 key.equals() 比對 → 找到返回 value關鍵認知:桶中儲存的是 鍵值對 Entry 物件,而非單純 value。這是理解碰撞處理與 get 邏輯的前提。
兩個不同 Key 的 hashCode() 相同,或計算桶索引後落在同一位置。HashMap 採用 鏈地址法:同桶位置以單向鏈表串連(JDK 8+ 鏈表長度 > 8 且陣列長度 > 64 時轉紅黑樹)。
// 簡化邏輯
Node<K,V> e = table[index];
while (e != null) {
if (e.hash == hash && (e.key == key || key.equals(e.key))) {
return e.value;
}
e = e.next;
}
先比 hash 再比 equals(),雙重保險定位精確 Entry。
預設負載因子 loadFactor = 0.75,當 size > threshold (capacity * loadFactor) 時觸發擴容,容量翻倍(2 的冪次)。
newTable = new Node[newCapacity]newIndex = (newCapacity - 1) & hash若兩執行緒同時發現需擴容,並發執行遷移,**頭插法導致鏈表成環**,後續 get/put 進入無限迴圈,CPU 100%。結論:HashMap 非執行緒安全,多執行緒環境必須用 ConcurrentHashMap 或 Collections.synchronizedMap()。
String、Integer、Long 等 final 類別,hashCode() 可快取,效能最佳equals() 相等 → hashCode() 必相等;反之不一定equals() 的欄位皆為 final,或放入 Map 後不再修改| 問題 | 核心回答 |
|---|---|
| HashMap vs Hashtable | 前者非同步、允許 null、效能高;後者同步、不允許 null、舊 API |
| HashMap vs ConcurrentHashMap | 後者分段鎖/CAS 實現高併發,JDK 8 放棄 Segment 改用 Node+CAS+synchronized |
| 為何負載因子 0.75 | 空間與時間平衡點:過小頻繁擴容,過大碰撞增加查找長度 |
| JDK 7 vs 8 差異 | JDK 8 引入紅黑樹、尾插法避免成環、擴容優化 |
| 可否用自訂物件作 Key | 可,但必須正確實作 hashCode/equals 且保證不可變 |
put("key", "value")
│
▼
hashCode() → 計算索引 → table[index]
│ │
│ ┌─────┴─────┐
│ ▼ ▼
│ 鏈表/樹 新建 Node
│ │
▼ equals() 比對
找到/未找到 → 更新/插入