Java 中 Map 按值排序

在 Java 中对一个 Map 按 Key 排序是很简单的一件事(TreeMap)。但是按 Value 排序的话,却略显麻烦。

在 Java 8 之前,对 Map 按 Value 排序一般有两种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 1. TreeMap
public class Testing {

public static void main(String[] args) {
HashMap<String, Integer> unsortMap = new HashMap<>();
ValueComparator comparator = new ValueComparator(unsortMap);
TreeMap<String, Integer> sortedMap = new TreeMap<>(comparator);

// put {A=9, B=2, C=7, D=1} into unsortMap

System.out.println("unsorted map: " + unsortMap);
sortedMap.putAll(unsortMap);
System.out.println("sorted map: " + sortedMap);
}
}

class ValueComparator implements Comparator<String> {

Map<String, Integer> base;

public ValueComparator(Map<String, Integer> base) {
this.base = base;
}

// this comparator imposes orderings that are inconsistent with equals.
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return 1;
} else {
return -1;
} // returning 0 would merge keys
}
}
// output:
// unsorted map: {A=9, B=2, C=7, D=1}
// sorted map: {D=1, B=2, C=7, A=9}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 2. add every Entry into List, sort the List and then put into LinkedHashMap
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<K, V>>() {

public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
return (o1.getValue()).compareTo(o2.getValue());
}
});

Map<K, V> result = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
// test: same as 1

在 Java 8 中,得益于新特性——Stream 和 Lambda 表达式,对 Map 按 Value 排序可以实现的更加优雅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
Map<String, Integer> unsortMap = new HashMap<>();

// put {A=9, B=2, C=7, D=1} into unsortMap

BinaryOperator<Integer> mergeFunction = (u, v) -> {
throw new IllegalStateException(String.format("Duplicate key %s", u));
};
Map<String, Integer> sortedMap = unsortMap.entrySet().stream()
.sorted(Entry.<String, Integer> comparingByValue().reversed())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, mergeFunction, LinkedHashMap::new));

System.out.println("unsorted map: " + unsortMap);
System.out.println("sorted map: " + sortedMap);
}
// output:
// unsorted map: {A=9, B=2, C=7, D=1}
// sorted map: {A=9, C=7, B=2, D=1}

核心代码仅3行,如果我们再对常用的LinkedHashMap Collector进行封装,代码就看起来更简洁舒服了。

不由感慨 Lambda 大法好~😄