核心結論
- TreeMap:天然按 Key 排序(紅黑樹),不支持直接按 Value 排序
- HashMap:無序,需轉換為 List/Stream 排序
- LinkedHashMap:可保持插入順序或訪問順序,常用於排序後結果收集
方案一:List + Collections.sort()(JDK 7 兼容)
Map map = new HashMap<>();
map.put("apple", 5);
map.put("banana", 3);
map.put("cherry", 20);
map.put("date", 8);
// 1. 轉為 List
List> list = new ArrayList<>(map.entrySet());
// 2. 自定義比較器(按 Value 升序)
list.sort((e1, e2) -> e1.getValue().compareTo(e2.getValue()));
// 或降序:e2.getValue().compareTo(e1.getValue())
// 3. 收集到 LinkedHashMap 保持順序
Map sortedMap = new LinkedHashMap<>();
for (Map.Entry entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
// 結果:{banana=3, apple=5, date=8, cherry=20}
方案二:Stream API(JDK 8+ 推薦)
按 Value 升序
Map sorted = map.entrySet().stream()
.sorted(Map.Entry.comparingByValue()) // 升序
// .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) // 降序
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> v1, // 鍵衝突合併策略(Value 不會衝突)
LinkedHashMap::new // 保持順序的 Map 實現
));
按 Value 降序 + 鍵值對象輸出
List> topList = map.entrySet().stream()
.sorted(Map.Entry.comparingByValue().reversed())
.limit(3) // Top 3
.collect(Collectors.toList());
// 輸出:[(cherry,20), (date,8), (apple,5)]
方案三:TreeMap 按 Key 排序(復習)
// 自然順序(升序)
Map treeMap = new TreeMap<>(map);
// 自定義 Key 比較器(降序)
Map descMap = new TreeMap<>((k1, k2) -> k2.compareTo(k1));
descMap.putAll(map);
性能與選型建議
| 方案 | 時間複雜度 | 適用場景 | JDK 版本 |
| List + sort | O(n log n) | JDK 7、需兼容舊代碼 | 1.7+ |
| Stream API | O(n log n) | 現代項目、鏈式操作、並行流 | 1.8+ |
| TreeMap (Key排序) | O(log n) 插入 | 需要實時保持 Key 有序 | 1.2+ |
常見陷阱
- 直接用 TreeMap 按 Value 排序:不可行,TreeMap 只按 Key 排序
- Collectors.toMap() 不指定 LinkedHashMap:默認返回 HashMap,順序丟失
- Value 為 null 導致 NPE:比較器需處理 null,或預先過濾
.filter(e -> e.getValue() != null)
- Value 重複導致鍵衝突:toMap 第三參數合併函數必須提供,否則拋異常
完整可運行 Demo
import java.util.*;
import java.util.stream.Collectors;
public class MapValueSortDemo {
public static void main(String[] args) {
Map scores = new HashMap<>();
scores.put("Alice", 88);
scores.put("Bob", 95);
scores.put("Charlie", 88); // 重複 Value
scores.put("David", 72);
scores.put("Eve", 91);
System.out.println("原始: " + scores);
// Stream 升序
Map asc = scores.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue,
(v1, v2) -> v1, LinkedHashMap::new));
System.out.println("升序: " + asc);
// Stream 降序 Top3
List> top3 = scores.entrySet().stream()
.sorted(Map.Entry.comparingByValue().reversed())
.limit(3)
.collect(Collectors.toList());
System.out.println("Top3: " + top3);
}
}
/* 輸出:
原始: {Alice=88, Bob=95, Charlie=88, David=72, Eve=91}
升序: {David=72, Alice=88, Charlie=88, Eve=91, Bob=95}
Top3: [Bob=95, Eve=91, Alice=88]
*/
總結
- 現代項目首選 Stream API,代碼簡潔、函數式、易並行
- 結果收集必須用 LinkedHashMap 保持順序
- 處理 Value 重複、null 值、大數據量 時注意邊界條件
- 若需實時按 Value 有序,考慮雙 Map 索引或專用數據結構(如 Guava Multimap)