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
差不多。