12.java8如何排序Map
最后更新于:2022-04-02 07:57:41
在这篇文章中,您将学习**如何使用Java对Map进行排序**。前几日有位朋友面试遇到了这个问题,看似很简单的问题,但是如果不仔细研究一下也是很容易让人懵圈的面试题。所以我决定写这样一篇文章。在Java中,有多种方法可以对Map进行排序,但是我们将重点介绍Java 8 Stream,这是实现目标的一种非常优雅的方法。
## 一、什么是Java 8 Stream
使用Java 8 Streams,我们可以按键和按值对映射进行排序。下面是它的工作原理:
![Java Stream函数式编程?用过都说好,案例图文详解送给你](http://cdn.zimug.com/javaStream1-2.jpg)
1. 将Map或List等集合类对象转换为Stream对象
2. 使用Streams的`sorted()`方法对其进行排序
3. 最终将其返回为`LinkedHashMap`(可以保留排序顺序)
`sorted()`方法以a`Comparator`作为参数,从而可以按任何类型的值对Map进行排序。如果对Comparator不熟悉,可以看本号前几天的文章,有一篇文章专门介绍了使用Comparator对List进行排序。
## 二、学习一下HashMap的merge()函数
在学习Map排序之前,有必要讲一下HashMap的merge()函数,该函数应用场景就是当Key重复的时候,如何处理Map的元素值。这个函数有三个参数:
* 参数一:向map里面put的键
* 参数二:向map里面put的值
* 参数三:如果键发生重复,如何处理值。可以是一个函数,也可以写成lambda表达式。
```
String k = "key";
HashMap map = new HashMap() {{
put(k, 1);
}};
map.merge(k, 2, (oldVal, newVal) -> oldVal + newVal);
```
看上面一段代码,我们首先创建了一个HashMap,并往里面放入了一个键值为k:1的元素。当我们调用merge函数,往map里面放入k:2键值对的时候,k键发生重复,就执行后面的lambda表达式。表达式的含义是:返回旧值oldVal加上新值newVal(1+2),现在map里面只有一项元素那就是k:3。
> 其实lambda表达式很简单:表示匿名函数,箭头左侧是参数,箭头右侧是函数体。函数的参数类型和返回值,由代码上下文来确定。
## 三、按Map的键排序
下面一个例子使用Java 8 Stream按Map的键进行排序:
~~~java
// 创建一个Map,并填入数据
Map codes = new HashMap<>();
codes.put("United States", 1);
codes.put("Germany", 49);
codes.put("France", 33);
codes.put("China", 86);
codes.put("Pakistan", 92);
// 按照Map的键进行排序
Map sortedMap = codes.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new
)
);
// 将排序后的Map打印
sortedMap.entrySet().forEach(System.out::println);
~~~
看上文中第二段代码:
* 首先使用entrySet().stream() 将Map类型转换为Stream流类型。
* 然后使用sorted方法排序,排序的依据是Map.Entry.comparingByKey(),也就是按照Map的键排序
* 最后用collect方法将Stream流转成LinkedHashMap。 其他参数都好说,重点看第三个参数,就是一个merge规则的lambda表达式,与merge方法的第三个参数的用法一致。由于本例中没有重复的key,所以新值旧值随便返回一个即可。
上面的程序将在控制台上打印以下内容,键(国家/地区名称)以自然字母顺序排序:
~~~plaintext
China=86
France=33
Germany=49
Pakistan=92
United States=1
~~~
> **请注意**使用`LinkedHashMap`来存储排序的结果以保持顺序。默认情况下,`Collectors.toMap()`返回`HashMap`。`HashMap`不能保证元素的顺序。
如果希望按照键进行逆向排序,加入下图中红色部分代码即可。
![](images/screenshot_1571800964528.png)
## 四、按Map的值排序
当然,您也可以使用Stream API按其值对Map进行排序:
~~~java
Map sortedMap2 = codes.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new));
sortedMap2.entrySet().forEach(System.out::println);
~~~
这是显示Map按值排序的输出:
~~~plaintext
United States=1
France=33
Germany=49
China=86
Pakistan=92
~~~
## 五、使用TreeMap按键排序
大家可能都知道TreeMap内的元素是有顺序的,所以利用TreeMap排序也是可取的一种方法。您需要做的就是创建一个`TreeMap`对象,并将数据从`HashMap`put到`TreeMap`中,非常简单:
~~~java
// 将 `HashMap` 转为 `TreeMap`
Map sorted = new TreeMap<>(codes);
~~~
这是输出:
~~~plaintext
China=86
France=33
Germany=49
Pakistan=92
United States=1
~~~
如上所示,键(国家/地区名称)以自然字母顺序排序。
## 最后:上文代码
```
String k = "key";
HashMap map = new HashMap() {{
put(k, 1);
}};
map.merge(k, 2, (oldVal, newVal) -> oldVal + newVal);
// 创建一个Map,并填入数据
Map codes = new HashMap<>();
codes.put("United States", 1);
codes.put("Germany", 49);
codes.put("France", 33);
codes.put("China", 86);
codes.put("Pakistan", 92);
// 按照Map的键进行排序
Map sortedMap = codes.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new
)
);
// 将排序后的Map打印
sortedMap.entrySet().forEach(System.out::println);
// sort the map by values
Map sorted = codes.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new));
sorted.entrySet().forEach(System.out::println);
```
';