Java Map 接口不保證順序。HashMap 無序、LinkedHashMap 維護插入/訪問順序、TreeMap 紅黑樹天然按鍵有序。本文系統講解三大 Map 特性、TreeMap 按鍵排序定製、通用按值排序轉換模式、性能基準與場景選型指南。
| 特性 | HashMap | LinkedHashMap | TreeMap |
|---|---|---|---|
| 底層結構 | 數組 + 鏈表/紅黑樹 (JDK 8+) | HashMap + 雙向鏈表 | 紅黑樹 |
| 順序保證 | 無序 (隨機) | 插入順序 / 訪問順序 (accessOrder) | 按鍵自然序 / Comparator 定製序 |
| 鍵值為 null | 允許 1 個 null key、多個 null value | 同 HashMap | 不允許 null key (拋 NPE),允許 null value |
| 線程安全 | 非線程安全 | 非線程安全 | 非線程安全 |
| 時間複雜度 | O(1) 平均 | O(1) 平均 | O(log n) |
| 遍歷性能 | 受容量影響 | 僅受元素數影響,穩定 | 有序遍歷,O(n) |
| 適用場景 | 高頻增刪改查、無序需求 | LRU 緩存、需保持輸入順序 | 範圍查詢、按鍵排序輸出、導航操作 |
Map map = new TreeMap<>(); // Integer 實現 Comparable
map.put(3, "C"); map.put(1, "A"); map.put(2, "B");
// 遍歷: 1=A, 2=B, 3=C
Map descMap = new TreeMap<>((k1, k2) -> k2 - k1); // 降序
// 或
Map customMap = new TreeMap<>((k1, k2) -> {
int cmp = k1.getPriority() - k2.getPriority();
return cmp != 0 ? cmp : k1.getId() - k2.getId(); // 次級排序
});
注意:Comparator 必須與 equals 一致 (即 compare(a,b)==0 <=> a.equals(b)),否則違反 SortedMap 契約,導致鍵「丟失」或重複。
NavigableMap nm = new TreeMap<>();
nm.lowerKey(5); // < 5 最大鍵
nm.floorKey(5); // ≤ 5 最大鍵
nm.ceilingKey(5); // ≥ 5 最小鍵
nm.higherKey(5); // > 5 最小鍵
nm.subMap(1, true, 10, false); // [1, 10) 範圍視圖
nm.headMap(5, true); // ≤ 5
nm.tailMap(5, false); // > 5
Map 無直接按值排序能力,核心思路:**EntrySet → List → Sort → LinkedHashMap**
public static > Map sortByValue(Map map) {
return map.entrySet().stream()
.sorted(Map.Entry.comparingByValue().reversed()) // 降序
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> v1, // 鍵衝突策略 (理論上不衝突)
LinkedHashMap::new // 保持插入順序
));
}
public static Map sortByValue(Map map, Comparator super V> valueComparator) {
List> entries = new ArrayList<>(map.entrySet());
entries.sort((e1, e2) -> valueComparator.compare(e1.getValue(), e2.getValue()));
Map result = new LinkedHashMap<>(entries.size() * 4 / 3 + 1); // 預分配避免擴容
for (Map.Entry entry : entries) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
// 調用
Map wordCount = ...;
Map sorted = sortByValue(wordCount, Comparator.reverseOrder());
public static > M sortByValue(M map, Comparator super V> cmp, Supplier mapFactory) {
return map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(cmp))
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue,
(v1, v2) -> v1, mapFactory));
}
// 調用
ConcurrentHashMap sorted = sortByValue(chMap, Comparator.naturalOrder(), ConcurrentHashMap::new);
| 操作 | HashMap | LinkedHashMap | TreeMap | 按值排序 (轉換) |
|---|---|---|---|---|
| 單次 put/get | ~50 ns | ~55 ns | ~120 ns | N/A |
| 遍歷 10萬條 | ~3 ms | ~2 ms | ~4 ms | ~15 ms (含排序) |
| 內存開銷 | 基準 | + 兩個引用/節點 | + 紅黑樹顏色/父指針 | + List + 新 Map |
| 線程安全變體 | ConcurrentHashMap | 無 (需同步包裝) | 無 (需同步包裝) | 同步包裝結果 Map |
NullPointerException。解決:用空字符串/特殊對象代替,或改用 ConcurrentSkipListMap (支援 null key)。put 不覆蓋舊值、get 找不到、size 異常。鐵律:compare(a,b)==0 <=> a.equals(b)。ORDER BY 下推。get/put 會移動節點,並發環境下極易導致鏈表斷裂/循環。並發場景用 ConcurrentHashMap + 獨立順序維護 (如時間戳)。按鍵排序用 TreeMap (自然序/Comparator/NavigableMap);按值排序轉 LinkedHashMap (Stream/List.sort/自定義 Comparator);保持插入順序用 LinkedHashMap。選型決策樹:
掌握 Map 順序本質與轉換模式,即可在性能、功能、線程安全三角中精準取捨。