原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/10748925.html

一、概述

Collector是专门用来作为Stream的collect方法的参数的。

public interface Stream<T> extends BaseStream<T, Stream<T>> {
<R, A> R collect(Collector<? super T, A, R> collector);
}

而Collectors是作为生产具体Collector的工具类。

二、Collector

Collector主要包含五个参数,它的行为也是由这五个参数来定义的,如下所示:

public interface Collector<T, A, R> {
// supplier参数用于生成结果容器,容器类型为A
Supplier<A> supplier();
// accumulator用于消费元素,也就是归纳元素,这里的T就是元素,它会将流中的元素一个一个与结果容器A发生操作
BiConsumer<A, T> accumulator();
// combiner用于两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A
BinaryOperator<A> combiner();
// finisher用于将之前整合完的结果R转换成为A
Function<A, R> finisher();
// characteristics表示当前Collector的特征值,这是个不可变Set
Set<Characteristics> characteristics();
}

Collector拥有两个of方法用于生成Collector实例,其中一个拥有上面所有五个参数,另一个四个参数,不包括finisher。

public interface Collector<T, A, R> {
// 四参方法,用于生成一个Collector,T代表流中的一个一个元素,R代表最终的结果
public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
BiConsumer<R, T> accumulator,
BinaryOperator<R> combiner,
Characteristics... characteristics) {/*...*/}
// 五参方法,用于生成一个Collector,T代表流中的一个一个元素,A代表中间结果,R代表最终结果,finisher用于将A转换为R
public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A, R> finisher,
Characteristics... characteristics) {/*...*/}
}

Characteristics:这个特征值是一个枚举,拥有三个值:CONCURRENT(多线程并行),UNORDERED(无序),IDENTITY_FINISH(无需转换结果)。其中四参of方法中没有finisher参数,所有必有IDENTITY_FINISH特征值。

三、Collectors

Collectors是一个工具类,是JDK预实现Collector的工具类,它内部提供了多种Collector,我们可以直接拿来使用,非常方便。

5.6.1 toCollection

将流中的元素全部放置到一个集合中返回,这里使用Collection,泛指多种集合。

public class CollectorsTest {
public static void toCollectionTest(List<String> list) {
List<String> ll = list.stream().collect(Collectors.toCollection(LinkedList::new));
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
toCollectionTest(list);
}
}

5.6.2 toList

将流中的元素放置到一个列表集合中去。这个列表默认为ArrayList。

public class CollectorsTest {
public static void toListTest(List<String> list) {
List<String> ll = list.stream().collect(Collectors.toList());
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
toListTest(list);
}
}

5.6.3 toSet

将流中的元素放置到一个无序集set中去。默认为HashSet。

public class CollectorsTest {
public static void toSetTest(List<String> list) {
Set<String> ss = list.stream().collect(Collectors.toSet());
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
toSetTest(list);
}
}

5.6.4 joining

joining的目的是将流中的元素全部以字符序列的方式连接到一起,可以指定连接符,甚至是结果的前后缀。

public class CollectorsTest {
public static void joiningTest(List<String> list){
// 无参方法
String s = list.stream().collect(Collectors.joining());
System.out.println(s);
// 指定连接符
String ss = list.stream().collect(Collectors.joining("-"));
System.out.println(ss);
// 指定连接符和前后缀
String sss = list.stream().collect(Collectors.joining("-","S","E"));
System.out.println(sss);
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
joiningTest(list);
}
}

执行结果:

1234567891101212121121asdaa3e3e3e2321eew
123-456-789-1101-212121121-asdaa-3e3e3e-2321eew
S123-456-789-1101-212121121-asdaa-3e3e3e-2321eewE

StringJoiner:这是一个字符串连接器,可以定义连接符和前后缀,正好适用于实现第三种joining方法。

5.6.5 mapping

这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳。

public class CollectorsTest {
public static void mapingTest(List<String> list){
List<Integer> ll = list.stream().limit(5).collect(Collectors.mapping(Integer::valueOf,Collectors.toList()));
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
mapingTest(list);
}
}

实例中截取字符串列表的前5个元素,将其分别转换为Integer类型,然后放到一个List中返回。

5.6.6 collectingAndThen

该方法是在归纳动作结束之后,对归纳的结果进行再处理。

public class CollectorsTest {
public static void collectingAndThenTest(List<String> list){
int length = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(),e -> e.size()));
System.out.println(length);
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
collectingAndThenTest(list);
}
}

执行结果为:

8

5.6.7 counting

该方法用于计数。

public class CollectorsTest {
public static void countingTest(List<String> list){
long size = list.stream().collect(Collectors.counting());
System.out.println(size);
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
countingTest(list);
}
}

结果:

8

5.6.8 minBy/maxBy

生成一个用于获取最小/最大值的Optional结果的Collector。

public class CollectorsTest {
public static void maxByAndMinByTest(List<String> list){
System.out.println(list.stream().collect(Collectors.maxBy((a,b) -> a.length()-b.length())));
System.out.println(list.stream().collect(Collectors.minBy((a,b) -> a.length()-b.length())));
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
maxByAndMinByTest(list);
}
}

执行结果为:

Optional[212121121]
Optional[123]

5.6.9 summingInt/summingLong/summingDouble

生成一个用于求元素和的Collector,首先通过给定的mapper将元素转换类型,然后再求和。

参数的作用就是将元素转换为指定的类型,最后结果与转换后类型一致。

public class CollectorsTest {
public static void summingTest(List<String> list){
int i = list.stream().limit(3).collect(Collectors.summingInt(Integer::valueOf));
long l = list.stream().limit(3).collect(Collectors.summingLong(Long::valueOf));
double d = list.stream().limit(3).collect(Collectors.summingDouble(Double::valueOf));
System.out.println(i +"\n" +l + "\n" + d);
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
summingTest(list);
}
}

执行结果为:

1368
1368
1368.0

5.6.10 averagingInt/averagingLong/averagingDouble

生成一个用于求元素平均值的Collector,首选通过参数将元素转换为指定的类型。

参数的作用就是将元素转换为指定的类型,求平均值涉及到除法操作,结果一律为Double类型。

public class CollectorsTest {
public static void averagingTest(List<String> list){
double i = list.stream().limit(3).collect(Collectors.averagingInt(Integer::valueOf));
double l = list.stream().limit(3).collect(Collectors.averagingLong(Long::valueOf));
double d = list.stream().limit(3).collect(Collectors.averagingDouble(Double::valueOf));
System.out.println(i +"\n" +l + "\n" + d);
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
averagingTest(list);
}
}

执行结果为:

456.0
456.0
456.0

5.6.11 reducing

reducing方法有三个重载方法,其实是和Stream里的三个reduce方法对应的,二者是可以替换使用的,作用完全一致,也是对流中的元素做统计归纳作用。

public final class Collectors {
// 无初始值的情况,返回一个可以生成Optional结果的Collector
public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op) {/*...*/}
// 有初始值的情况,返回一个可以直接产生结果的Collector
public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op) {/*...*/}
// 有初始值,还有针对元素的处理方案mapper,生成一个可以直接产生结果的Collector,元素在执行结果操作op之前需要先执行mapper进行元素转换操作
public static <T, U> Collector<T, ?, U> reducing(U identity,
Function<? super T, ? extends U> mapper,
BinaryOperator<U> op) {/*...*/}
}

实例:

public class CollectorsTest {
public static void reducingTest(List<String> list){
System.out.println(list.stream().limit(4).map(String::length).collect(Collectors.reducing(Integer::sum)));
System.out.println(list.stream().limit(3).map(String::length).collect(Collectors.reducing(0, Integer::sum)));
System.out.println(list.stream().limit(4).collect(Collectors.reducing(0,String::length,Integer::sum)));
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
reducingTest(list);
}
}
Optional[13]
9
13

效果可参见Java基础系列-Stream

5.6.12 groupingBy

这个方法是用于生成一个拥有分组功能的Collector,它也有三个重载方法:

public final class Collectors {
// 只需一个分组参数classifier,内部自动将结果保存到一个map中,每个map的键为?类型(即classifier的结果类型),值为一个list,这个list中保存在属于这个组的元素。
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(
Function<? super T, ? extends K> classifier) {/*...*/}
// 在上面方法的基础上增加了对流中元素的处理方式的Collector,比如上面的默认的处理方法就是Collectors.toList()
public static <T, K, A, D>Collector<T, ?, Map<K, D>> groupingBy(
Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream) {/*...*/}
// 在第二个方法的基础上再添加了结果Map的生成方法。
public static <T, K, D, A, M extends Map<K, D>>
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream) {/*...*/}
}

实例:

public class CollectorsTest {
public static void groupingByTest(List<String> list){
Map<Integer,List<String>> s = list.stream().collect(Collectors.groupingBy(String::length));
Map<Integer,List<String>> ss = list.stream().collect(Collectors.groupingBy(String::length, Collectors.toList()));
Map<Integer,Set<String>> sss = list.stream().collect(Collectors.groupingBy(String::length,HashMap::new,Collectors.toSet()));
System.out.println(s.toString() + "\n" + ss.toString() + "\n" + sss.toString());
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
groupingByTest(list);
}
}

执行结果为:

{3=[123, 456, 789], 4=[1101], 5=[asdaa], 6=[3e3e3e], 7=[2321eew], 9=[212121121]}
{3=[123, 456, 789], 4=[1101], 5=[asdaa], 6=[3e3e3e], 7=[2321eew], 9=[212121121]}
{3=[123, 456, 789], 4=[1101], 5=[asdaa], 6=[3e3e3e], 7=[2321eew], 9=[212121121]}

groupingBy方法还有并发版的groupingByConcurrent,功能基本一致,只是返回的Collector是并行的。

5.6.13 partitioningBy

该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的列表List。

该方法有两个重载方法:

public final class Collectors {
// 只需一个校验参数predicate
public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {/*...*/}
// 在上面方法的基础上增加了对流中元素的处理方式的Collector,比如上面的默认的处理方法就是Collectors.toList()
public static <T, D, A>
Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
Collector<? super T, A, D> downstream) {/*...*/}
}

实例:

public class CollectorsTest {
public static void partitioningByTest(List<String> list){
Map<Boolean,List<String>> map = list.stream().collect(Collectors.partitioningBy(e -> e.length()>5));
Map<Boolean,Set<String>> map2 = list.stream().collect(Collectors.partitioningBy(e -> e.length()>6,Collectors.toSet()));
System.out.println(map.toString() + "\n" + map2.toString());
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
partitioningByTest(list);
}
}

执行结果:

{false=[123, 456, 789, 1101, asdaa], true=[212121121, 3e3e3e, 2321eew]}
{false=[123, 456, 1101, 789, 3e3e3e, asdaa], true=[212121121, 2321eew]}

5.6.14 toMap

toMap方法是根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,可以指定出现重复键时的处理方案和保存结果的map。

public final class Collectors {
// 指定键和值的生成方式keyMapper和valueMapper
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {/*...*/}
// 在上面方法的基础上增加了对键发生重复时处理方式的mergeFunction,比如上面的默认的处理方法就是抛出异常
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction) {/*...*/}
// 在第二个方法的基础上再添加了结果Map的生成方法。
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier) {/*...*/}
}

实例:

public class CollectorsTest {
public static void toMapTest(List<String> list){
Map<String,String> map = list.stream().limit(3).collect(Collectors.toMap(e -> e.substring(0,1),e -> e));
Map<String,String> map1 = list.stream().collect(Collectors.toMap(e -> e.substring(0,1),e->e,(a,b)-> b));
Map<String,String> map2 = list.stream().collect(Collectors.toMap(e -> e.substring(0,1),e->e,(a,b)-> b,HashMap::new));
System.out.println(map.toString() + "\n" + map1.toString() + "\n" + map2.toString());
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
toMapTest(list);
}
}

执行结果:

{1=123, 4=456, 7=789}
{a=asdaa, 1=1101, 2=2321eew, 3=3e3e3e, 4=456, 7=789}
{a=asdaa, 1=1101, 2=2321eew, 3=3e3e3e, 4=456, 7=789}

第一种方式中,如果不添加limit限制,就会抛出异常。

还有并发的版本:toConcurrentMap,同样三种重载方法,与toMap基本一致,只是它最后使用的map是并发Map:ConcurrentHashMap。

5.6.15 summarizingInt/summarizingLong/summarizingDouble

这三个方法适用于汇总的,返回值分别是IntSummaryStatistics,LongSummaryStatistics,DoubleSummaryStatistics。

在这些返回值中包含有流中元素的指定结果的数量、和、最大值、最小值、平均值。所有仅仅针对数值结果。

public class CollectorsTest {
public static void summarizingTest(List<String> list){
IntSummaryStatistics intSummary = list.stream().collect(Collectors.summarizingInt(String::length));
LongSummaryStatistics longSummary = list.stream().limit(4).collect(Collectors.summarizingLong(Long::valueOf));
DoubleSummaryStatistics doubleSummary = list.stream().limit(3).collect(Collectors.summarizingDouble(Double::valueOf));
System.out.println(intSummary.toString() + "\n" + longSummary.toString() + "\n" + doubleSummary.toString());
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
summarizingTest(list);
}
}

执行结果:

IntSummaryStatistics{count=8, sum=40, min=3, average=5.000000, max=9}
LongSummaryStatistics{count=4, sum=2469, min=123, average=617.250000, max=1101}
DoubleSummaryStatistics{count=3, sum=1368.000000, min=123.000000, average=456.000000, max=789.000000}

最后我们可以从返回的汇总实例中获取到想要的汇总结果。

四、总结

整个Collectors工具类就是在为Collector服务,用于创建各种不同的Collector。部分功能与Stream中的方法重合了,为了简化代码,完全不必采用Collectors实现,优先Stream方法。

参考:

Java基础系列-Collector和Collectors的更多相关文章

  1. 2015年12月28日 Java基础系列(六)流

    2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流

  2. Java基础系列--static关键字

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/8477914.html 一.概述 static关键字是Java诸多关键字中较常使用的一个,从 ...

  3. Java基础系列-ArrayList

    原创文章,转载请标注出处:<Java基础系列-ArrayList> 一.概述 ArrayList底层使用的是数组.是List的可变数组实现,这里的可变是针对List而言,而不是底层数组. ...

  4. Java基础系列-二进制操作

    原创文章,转载请标注出处:<Java基础系列-二进制操作> 概述 Java源码中涉及到大量的二进制操作,非常的复杂,但非常的快速. Java二进制表示法 首先了解下二进制,二进制是相对十进 ...

  5. Java基础系列-equals方法和hashCode方法

    原创文章,转载请标注出处:<Java基础系列-equals方法和hashCode方法> 概述         equals方法和hashCode方法都是有Object类定义的. publi ...

  6. Java基础系列-Comparable和Comparator

    原创文章,转载请标注出处:<Java基础系列-Comparable和Comparator> 一.概述         Java中的排序是由Comparable和Comparator这两个接 ...

  7. Java基础系列--HashMap(JDK1.8)

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/10022092.html Java基础系列-HashMap 1.8 概述 HashMap是 ...

  8. 夯实Java基础系列1:Java面向对象三大特性(基础篇)

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 [https://github.com/h2pl/Java-Tutorial](https: ...

  9. 夯实Java基础系列3:一文搞懂String常见面试题,从基础到实战,更有原理分析和源码解析!

    目录 目录 string基础 Java String 类 创建字符串 StringDemo.java 文件代码: String基本用法 创建String对象的常用方法 String中常用的方法,用法如 ...

随机推荐

  1. springboot读取自定义配置文件节点

    今天和大家分享的是自定义配置信息的读取:近期有写博客这样的计划,分别交叉来写springboot方面和springcloud方面的文章,因为springboot预计的篇章很多,这样cloud的文章就需 ...

  2. 基于 Maven 的多模块 Java ( Spring ) 项目构建

    索引: 开源Spring解决方案--lm.solution 参看代码 GitHub: solution/pom.xml pojo/pom.xml mapper/pom.xml common/pom.x ...

  3. 咸鱼Chen

    关于我 网名:咸鱼Chen 英文:nick chen 签名:I'm nothing but I must be everything. 标签:Python爱好(ma)者(nong),干过后端开发.算法 ...

  4. 深入学习Redis(5):集群

    前言 在前面的文章中,已经介绍了Redis的几种高可用技术:持久化.主从复制和哨兵,但这些方案仍有不足,其中最主要的问题是存储能力受单机限制,以及无法实现写操作的负载均衡. Redis集群解决了上述问 ...

  5. git程序多版本维护方案

    目前所在公司开发的系统为一个基础版本(通用版)包含了行业内一些基础功能实现,后期根据不同厂家进行定制版的开发,考虑独立项目的话代码维护不太方便,并且如果通用版本有变动的话,其他定制版本也都需要进行变动 ...

  6. Service Fabric service 根据环境变量读取配置文件

    前言 一个服务或者产品,往往需要三个环境:一个开发环境(Development),一个测试环境(Staging),一个生产环境(Production), 这就不可避免的需要多个配置文件来匹配相应的环境 ...

  7. vue学习记录⑤(组件通信-父与子)

    今天我们看一下组件通信. 经过前面几篇文章,我们已经可以构建出完整的单个组件,并利用路由使其串联起来访问了. 但这明显还是不够的.一个页面不可能就是个单组件,一般是由多个组件合成的.正因为如此,组件之 ...

  8. 使用d3.v5实现条形图

    效果图: 条形图: 目录结构: <!DOCTYPE html> <html lang="en"> <head> <meta charset ...

  9. 浅析C语言中的整形类型

    在C语言中,可以把 字符型.短整形.整形.长整形都看作是整形,同属于整形家族这个大类型. 这些类型的大小,默认是否有符号等一些知识点较零散,较容易混淆,所以特地整理如下.   一 类型存储字节长度说明 ...

  10. python3 生成器初识 NLP第五条

    话不多说,先把第五条抄一遍: 五,沟通的意义在于对方的回应 沟通没有对与错,只有“有效果”或者“没有效果”之分. 自己说得多“对”没有意义,对方收到你想表达的讯息才是沟通的意义. 因此自己说什么不重要 ...