Map<K,V> 是鍵值對集合的核心介面,**鍵唯一、值可重複**。本文聚焦三大主流實作——HashMap、LinkedHashMap、TreeMap——的底層原理、效能特性、選型場景與常見陷阱,並補充 Java 8+ 優化與併發替代方案。
| 特性 | HashMap | LinkedHashMap | TreeMap |
|---|---|---|---|
| 底層結構 | 陣列 + 鏈表/紅黑樹 (JDK 8) | HashMap + 雙向鏈表 | 紅黑樹 |
| 順序保證 | 無序(隨機) | **插入順序** 或 **存取順序** | **鍵自然順序** 或 **Comparator** |
| Null 鍵/值 | 允許 1 個 null key、多個 null value | 允許 | **不允許 null key**(拋 NPE),允許 null value |
| 執行緒安全 | 非安全 | 非安全 | 非安全 |
| get/put 複雜度 | O(1) 平均,O(log n) 最壞 (樹化) | O(1) | O(log n) |
| 記憶體開銷 | 中 | 高(額外雙向指標) | 高(樹節點) |
| 典型場景 | 通用緩存、計數、索引 | LRU 快取、有序配置、JSON 序列化保序 | 範圍查詢、排序字典、NavigableMap 操作 |
transient Node[] table; // 桶陣列,長度永遠 2 的冪
transient int size; // 鍵值對數量
int threshold; // 擴容門檻 = capacity * loadFactor
final float loadFactor; // 預設 0.75
int modCount; // 結構修改次數,fail-fast 依據
hash(key) ^ (hash >>> 16) 混合高低位,減少碰撞。(n - 1) & hash(位運算替代取模,要求 n 為 2 的冪)。TREEIFY_THRESHOLD (8) 且 capacity ≥ MIN_TREEIFY_CAPACITY (64) → 紅黑樹,查詢 O(log n)。UNTREEIFY_THRESHOLD (6) → 還原鏈表。rehash 重新分佈(高位決定新桶:原位或原位 + oldCap)。
V put(K key, V value) // 放入,返回舊值
V get(Object key) // 取值
V getOrDefault(Object key, V defaultVal) // 取值或預設 (JDK 8)
V putIfAbsent(K key, V value) // 僅無鍵時放入
V computeIfAbsent(K key, Function) // 無鍵則計算並放入(原子性)
V computeIfPresent(K key, BiFunction) // 有鍵則重新計算
V merge(K key, V value, BiFunction) // 合併邏輯:新值 + 舊值 → 結果
boolean containsKey(Object key)
boolean containsValue(Object value)
void forEach(BiConsumer) // 遍歷
new LinkedHashMap<>(),適合保持配置檔、JSON 輸出順序。new LinkedHashMap<>(16, 0.75f, true),**最近最少使用 (LRU) 在頭部**,適合實作快取淘汰。
// LRU 快取範本
class LRUCache extends LinkedHashMap {
private final int maxSize;
LRUCache(int maxSize) { super(maxSize, 0.75f, true); this.maxSize = maxSize; }
@Override protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > maxSize; // 超過容量自動移除最久未用
}
}
// 自然順序:Key 實作 Comparable
TreeMap tm1 = new TreeMap<>(); // 字母序
// 自定義比較器:降序
TreeMap tm2 = new TreeMap<>((a,b) -> b.compareTo(a));
Map.Entry firstEntry(); // 最小鍵
Map.Entry lastEntry(); // 最大鍵
Map.Entry lowerEntry(K key); // strictly < key
Map.Entry floorEntry(K key); // ≤ key
Map.Entry higherEntry(K key); // strictly > key
Map.Entry ceilingEntry(K key); // ≥ key
NavigableMap subMap(K from, boolean incF, K to, boolean incT); // 範圍視圖
NavigableMap headMap(K to, boolean inc); // < to
NavigableMap tailMap(K from, boolean inc); // ≥ from
equals/hashCode 相關欄位 → 找不到條目。**鍵必須不可變**(String、Integer、自訂 final 類)。ConcurrentHashMap(分段鎖/CAS,高併發讀寫)Collections.synchronizedMap(粗粒度鎖,低併發)new HashMap<>(expectedSize * 4 / 3 + 1) 避免多次擴容。
Iterator> it = map.entrySet().iterator();
while (it.hasNext()) { if (cond) it.remove(); }
HashMap 實作 Serializable,但 transient table 需自定 writeObject/readObject 重建,大量資料建議改用 JSON/Protobuf。
// 合併計數
map.merge(word, 1, Integer::sum);
// 分組統計
Map freq = list.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
// 過濾
map.entrySet().removeIf(e -> e.getValue() < threshold);
// 轉換
Map upper = map.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toUpperCase()));
選 HashMap 做通用高效查找,選 LinkedHashMap 保順序或做 LRU,選 TreeMap 需排序/範圍查詢。牢記**鍵不可變、併發用 ConcurrentHashMap、初始容量預估**,Map 就能穩穩支撐你的業務邏輯。