51好读  ›  专栏  ›  码农小胖哥

Java 8 Stream 的终极技巧——Collectors 操作

码农小胖哥  · 掘金  ·  · 2020-01-02 14:03

正文

阅读 115

Java 8 Stream 的终极技巧——Collectors 操作

1. 前言

昨天在 Collection移除元素操作 相关的文章中提到了 Collectors 。相信很多同学对这个比较感兴趣,那我们今天就来研究一下 Collectors

2. Collectors 的作用

Collectors Java 8 加入的操作类,位于 java.util.stream 包下。它会根据不同的策略将元素收集归纳起来,比如最简单常用的是将元素装入 Map Set List 等可变容器中。特别对于 Java 8 Stream Api 来说非常有用。它提供了 collect() 方法来对 Stream 流进行终结操作派生出基于各种策略的结果集。我们就借助于 Stream 来熟悉一下 Collectors 吧。我们依然用昨天的例子:

    List<String> servers = new ArrayList<>();
        servers.add("Felordcn");
        servers.add("Tomcat");
        servers.add("Jetty");
        servers.add("Undertow");
        servers.add("Resin");
复制代码

3. Java 8 中 Collectors 的方法

Collectors 提供了一系列的静态方法供我们使用,通常情况我们静态导入即可使用。接下来我们来看看都提供了哪些方法吧。

3.1 类型归纳

这是一个系列,作用是将元素分别归纳进可变容器 List Map Set Collection 或者 ConcurrentMap

    Collectors.toList();
    Collectors.toMap();
    Collectors.toSet();
    Collectors.toCollection();
    Collectors.toConcurrentMap();
复制代码

我们可以根据以上提供的 API 使用 Stream collect 方法中的转换为熟悉的集合容器。非常简单这里不再演示。

3.2 joining

将元素以某种规则连接起来。该方法有三种重载 joining(CharSequence delimiter) joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)

 //   输出 FelordcnTomcatJettyUndertowResin
 servers.stream().collect(Collectors.joining());

 //   输出 Felordcn,Tomcat,Jetty,Undertow,Resin
 servers.stream().collect(Collectors.joining("," ));

 //   输出 [Felordcn,Tomcat,Jetty,Undertow,Resin]
 servers.stream().collect(Collectors.joining(",", "[", "]")); 
复制代码

用的比较多的是读取 HttpServletRequest 中的 body

  HttpServletRequest.getReader().lines().collect(Collectors.joining());
复制代码

3.3 collectingAndThen

该方法先执行了一个归纳操作,然后再对归纳的结果进行 Function 函数处理输出一个新的结果。

 // 比如我们将servers joining 然后转成大写,结果为: FELORDCN,TOMCAT,JETTY,UNDERTOW,RESIN   
 servers.stream.collect(Collectors.collectingAndThen(Collectors.joining(","), String::toUpperCase));
复制代码

3.4 groupingBy

按照条件对元素进行分组,和 SQL 中的 group by 用法有异曲同工之妙,通常也建议使用 Java 进行分组处理以减轻数据库压力。 groupingBy 也有三个重载方法 我们将 servers 按照长度进行分组:

// 按照字符串长度进行分组    符合条件的元素将组成一个 List 映射到以条件长度为key 的 Map<Integer, List<String>> 中
servers.stream.collect(Collectors.groupingBy(String::length))
复制代码

如果我不想 Map value List 怎么办? 上面的实现实际上调用了下面的方式:

 //Map<Integer, Set<String>>
 servers.stream.collect(Collectors.groupingBy(String::length, Collectors.toSet()))
复制代码

我要考虑同步安全问题怎么办? 当然使用线程安全的同步容器啊,那前两种都用不成了吧! 别急! 我们来推断一下,其实第二种等同于下面的写法:

 Supplier<Map<Integer,Set<String>>> mapSupplier = HashMap::new;
 Map<Integer,Set<String>> collect = servers.stream.collect(Collectors.groupingBy(String::length, mapSupplier, Collectors.toSet()));
复制代码

这就非常好办了,我们提供一个同步 Map 不就行了,于是问题解决了:

 Supplier<Map<Integer, Set<String>>> mapSupplier = () -> Collections.synchronizedMap(new HashMap<>());
 Map<Integer, Set<String>> collect = servers.stream.collect(Collectors.groupingBy(String::length, mapSupplier, Collectors.toSet()));
复制代码

其实同步安全问题 Collectors 的另一个方法 groupingByConcurrent 给我们提供了解决方案。用法和 groupingBy 差不多。







请到「今天看啥」查看全文