乐趣区

恕我直言你可能真的不会java第12篇如何使用Stream-API对Map类型元素排序

在这篇文章中,您将学习 如何使用 Java 对 Map 进行排序。前几日有位朋友面试遇到了这个问题,看似很简单的问题,但是如果不仔细研究一下也是很容易让人懵圈的面试题。所以我决定写这样一篇文章。在 Java 中,有多种方法可以对 Map 进行排序,但是我们将重点介绍 Java 8 Stream,这是实现目标的一种非常优雅的方法。

一、什么是 Java 8 Stream

使用 Java 8 Streams,我们可以按键和按值对映射进行排序。下面是它的工作原理:

  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<String, Integer> map = new HashMap<String, Integer>() {{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 的键进行排序:

// 创建一个 Map,并填入数据
Map<String, Integer> 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<String, Integer> 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,所以新值旧值随便返回一个即可。

上面的程序将在控制台上打印以下内容,键(国家 / 地区名称)以自然字母顺序排序:

China=86
France=33
Germany=49
Pakistan=92
United States=1

请注意 使用 LinkedHashMap 来存储排序的结果以保持顺序。默认情况下,Collectors.toMap()返回 HashMapHashMap 不能保证元素的顺序。

如果希望按照键进行逆向排序,加入下图中红色部分代码即可。

四、按 Map 的值排序

当然,您也可以使用 Stream API 按其值对 Map 进行排序:

Map<String, Integer> 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 按值排序的输出:

United States=1
France=33
Germany=49
China=86
Pakistan=92

五、使用 TreeMap 按键排序

大家可能都知道 TreeMap 内的元素是有顺序的,所以利用 TreeMap 排序也是可取的一种方法。您需要做的就是创建一个 TreeMap 对象,并将数据从 HashMapput 到TreeMap 中,非常简单:

// 将 `HashMap` 转为 `TreeMap`
Map<String, Integer> sorted = new TreeMap<>(codes);

这是输出:

China=86
France=33
Germany=49
Pakistan=92
United States=1

如上所示,键(国家 / 地区名称)以自然字母顺序排序。

欢迎关注我的博客,里面有很多精品合集

  • 本文转载注明出处(必须带连接,不能只转文字):字母哥博客。

觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力!。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。

  • 《手摸手教你学 Spring Boot2.0》
  • 《Spring Security-JWT-OAuth2 一本通》
  • 《实战前后端分离 RBAC 权限管理系统》
  • 《实战 SpringCloud 微服务从青铜到王者》
  • 《VUE 深入浅出系列》
退出移动版