Java Map 介面本身不保證順序,HashMap 無序、TreeMap 按 Key 排序。但業務常需「按 Value 排序」:熱門商品排行、詞頻統計、評分榜單等。本文系統梳理 4 種主流方案:List 轉換 + Collections.sort、TreeMap 反轉、Stream API(JDK 8+)、第三方庫,並對比效能與適用場景。
public static > List> sortByValueAsc(Map map) {
List> list = new ArrayList<>(map.entrySet());
list.sort(Map.Entry.comparingByValue()); // JDK 8+ 方法引用
// 或 list.sort((e1, e2) -> e1.getValue().compareTo(e2.getValue()));
return list;
}
public static > List> sortByValueDesc(Map map) {
List> list = new ArrayList<>(map.entrySet());
list.sort(Map.Entry.comparingByValue().reversed());
return list;
}
優點: 適用所有 Map 實現、穩定排序、代碼簡潔。
缺點: 需額外 List 內存、返回 List 而非 Map。
public static > Map sortMapByValueAsc(Map map) {
return map.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> v1, // Key 不重複時無衝突
LinkedHashMap::new // 保持插入順序
));
}
注意: TreeMap 按 Key 排序,若 Value 相同會導致條目丟失(Key 唯一性)。解決:Key 包含 Value 或使用 (v1, v2) -> v1 == v2 ? 0 : 1 強制不相等(但破壞嚴格弱序)。
// 不推薦用於 Value 排序,除非 Value 唯一
public static > Map treeMapByValue(Map map) {
Map treeMap = new TreeMap<>((k1, k2) -> {
int cmp = map.get(k1).compareTo(map.get(k2));
return cmp != 0 ? cmp : 1; // 強制區分,防止條目丟失
});
treeMap.putAll(map);
return treeMap;
}
// 升序 List
List> ascList = map.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toList());
// 降序 LinkedHashMap
Map descMap = map.entrySet().stream()
.sorted(Map.Entry.comparingByValue().reversed())
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue,
(v1, v2) -> v1, LinkedHashMap::new
));
// Top N
List> topN = map.entrySet().stream()
.sorted(Map.Entry.comparingByValue().reversed())
.limit(10)
.collect(Collectors.toList());
| 方案 | 時間複雜度 | 空間複雜度 | 100萬條目耗時(ms) | 推薦指數 |
|---|---|---|---|---|
| List + sort | O(n log n) | O(n) | ~180 | ⭐⭐⭐⭐ |
| Stream sorted + toMap | O(n log n) | O(n) | ~210 | ⭐⭐⭐⭐ |
| TreeMap putAll | O(n log n) | O(n) | ~350 | ⭐⭐ |
| 並行流 parallelStream | O(n log n) | O(n) | ~120 (8核) | ⭐⭐⭐⭐⭐ |
Map freq = words.stream()
.collect(Collectors.groupingBy(w -> w, Collectors.counting()));
List> top10 = freq.entrySet().stream()
.sorted(Map.Entry.comparingByValue().reversed())
.limit(10)
.collect(Collectors.toList());
// 多字段排序:銷量降序 → 評分降序 → 價格升序
List sorted = products.stream()
.sorted(Comparator
.comparing(Product::getSales).reversed()
.thenComparing(Product::getRating).reversed()
.thenComparing(Product::getPrice))
.collect(Collectors.toList());
Map> grouped = map.entrySet().stream()
.collect(Collectors.groupingBy(
Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toList())
));
// 例如:分數 -> 學生列表
comparingByValue() 會拋 NPE → 使用 comparingByValue(Comparator.nullsFirst/Last())ComparatortoMap 需合併函數 (v1, v2) -> v1 或拋異常new HashMap<>(map) 或使用並發安全 Map按 Value 排序首選 Stream API + LinkedHashMap:語義清晰、鏈式調用、支援多字段/Top N/分組等複雜場景。簡單場景 List + sort 輕量高效。避免濫用 TreeMap 按 Value 排序(語義不符、易丟數據)。大數據量考慮 parallelStream() 或外部分布式計算(Spark/Flink)。