Collector介绍

Java8的stream api能很方便我们对数据进行统计分类等工作,函数式编程的风格让我们方便并且直观地编写统计代码。

例如:

Stream stream = Stream.iterate(1, item-> item+2).limit(6);

// stream.filter(item -> item > 2).mapToInt(item -> item * 2).skip(2).limit(2).sum();//.reduce(0, (val, val2)->val+val2); // System.out.println(sum); IntSummaryStatistics summaryStatistics = stream.filter(item -> item > 2).mapToInt(item -> item * 2).skip(2).summaryStatistics(); System.out.println(summaryStatistics.getAverage());

stream里有一个collect(Collector c)方法,这个方法里面接收一个Collector的实例。这里我们要弄清楚Collector与Collectors之间的关系。

作为collect方法的参数,Collector是一个接口,它是一个可变的汇聚操作,将输入元素累计到一个可变的结果容器中;它会在所有元素都处理完毕后,将累积的结果转换为一个最终的表示(这是一个可选操作);

这些如果你不太懂,请继续往下看,结合下面自定义Collector,相信你可以理解这些内容。

Collectors本身提供了关于Collector的常见汇聚实现,Collectors的内部类CollectorImpl实现了Collector接口,Collectors本身实际上是一个工厂。

Collector的使用

很多时候我们会用到Collectors的toList方法,Collectors中提供了将流中元素累积到汇聚结果的各种方式,例如Counting、Joining、maxBy等等。下面是一个例子:

//分组

List list1 = Arrays.asList(s1, s2, s3, s4, s5);

//根据名称分组

// Map> map = list1.stream().collect(Collectors.groupingBy(Student::getName));

//先根据名称分组,然后求每组平均分

Map map = list1.stream().collect(Collectors.groupingBy(Student::getName, Collectors.averagingDouble(Student::getScore)));

System.out.println(map);

//分区

Map> map1 = list1.stream().collect(Collectors.partitioningBy(item -> item.getScore() >= 90));

System.out.println(map1);

System.out.println("------2-----");

//先根据名称分组再根据分数分组

Map>> map2 = list1.stream().collect(Collectors.groupingBy(Student::getName, Collectors.groupingBy(Student::getScore)));

System.out.println(map2);

这里分区分组与数据库中的分区分组类似。

Collectors are designed to be composed; many of the methods in {@link Collectors} are functions that take a collector and produce a new collector.

附上javadoc上的一句话,这句话说明收集操作是可以嵌套的。

自定义Collector

前面讲过,Collectors本身提供了关于Collector的常见汇聚实现,那么程序员自身也可以根据情况定义自己的汇聚实现。

首先我们看下Collector接口的结构

public interface Collector {

Supplier supplier();

BiConsumer accumulator();

BinaryOperator combiner();

Function finisher();

Set characteristics();

}

其中这里的泛型所表示的含义是:

T:表示流中每个元素的类型。

A:表示中间结果容器的类型。

R:表示最终返回的结果类型。

Collector中还定义了一个枚举类Characteristics,有三个枚举值,理解这三个值的含义对于我们自己编写正确的收集器也是至关重要的。

Characteristics.CONCURRENT:表示中间结果只有一个,即使在并行流的情况下。所以只有在并行流且收集器不具备CONCURRENT特性时,combiner方法返回的lambda表达式才会执行(中间结果容器只有一个就无需合并)。

Characteristics.UNORDER:表示流中的元素无序。

Characteristics.IDENTITY_FINISH:表示中间结果容器类型与最终结果类型一致,此时finiser方法不会被调用。

我们再来看一下Collectors中toList方法的实现。

public static Collector> toList() {

return new CollectorImpl<>((Supplier>) ArrayList::new, List::add,

(left, right) -> { left.addAll(right); return left; },

CH_ID);

}

static final Set CH_ID

= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));

到此,不知你是否对自定义收集器有那么点感觉了?

那么到底怎么实现自定义收集器呢,下面举例子来看看。

public class MyCollectorImpl implements Collector, Set> {

@Override

public Supplier> supplier() {

return HashSet::new;

}

@Override

public BiConsumer, T> accumulator() {

return Set::add;

}

@Override

public BinaryOperator> combiner() {

return (set, item) -> {set.addAll(item); return set;};

}

@Override

public Function, Set> finisher() {

return Function.identity();

}

@Override

public Set characteristics() {

return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH,UNORDERED));

}

public static void main(String[] args) {

List list = Arrays.asList("hello", "world", "welcome");

Set set = list.stream().collect(new MyCollectorImpl<>());

set.forEach(System.out::println);

}

}

这是一个简单的例子,我们给收集器加上了IDENTITY_FINISH特性,此时finisher方法返回的lambda表达式是不会得到调用的。这一点也可以从源码中得到验证。

例2:

public class MyCollectorImpl2 implements Collector, Map>{

@Override

public Supplier> supplier() {

return HashSet::new;

}

@Override

public BiConsumer, T> accumulator() {

return Set::add;

}

@Override

public BinaryOperator> combiner() {

return (set1, set2)->{

set1.addAll(set2);

return set1;

};

}

@Override

public Function, Map> finisher() {

return (set)->{

HashMap map = new HashMap();

set.stream().forEach((item)->map.put(item, item)); return map; }; } @Override public Set characteristics() { return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED)); } public static void main(String[] args) { List list = Arrays.asList("hello", "world", "welcome"); HashSet set = new HashSet<>(); set.addAll(list); Map map = set.stream().collect(new MyCollectorImpl2<>()); System.out.println(map); } }

这个例子中由于中间累计结果容器的类型与最终类型不一致,在finisher方法中必须做正确的处理,否则肯定抛出类型转换的异常。

java中collector使用_Java8中重要的收集器collector相关推荐

  1. java收集器Collector

    一.收集器Collector //T:表示流中每个元素的类型. A:表示中间结果容器的类型. R:表示最终返回的结果类型. public interface Collector<T, A, R& ...

  2. Java虚拟机(三)--------GC算法和收集器

    如何判断对象可以被回收 堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的 对象) 引用计数法 给对象添加一个引用计数器,每当有一个地方引用,计数器 ...

  3. Java8中重要的收集器Collector

    Collector介绍 Java8的stream api能很方便我们对数据进行统计分类等工作,函数式编程的风格让我们方便并且直观地编写统计代码. 例如: Stream<Integer> s ...

  4. 35.JVM 参数(JVM中的各种参数及含义、收集器相关、PrintGC等各种辅助参数)

    35.JVM 参数 说下下面jvm参数含义: -server -Xms512m -Xmx512m -Xss1024K -XX:PermSize=256m -XX:MaxPermSize=512m - ...

  5. Java8 Stream 自定义收集器Collector

    在之前的例子中,我们都是使用Collectors的静态方法提供的CollectorImpl,为接口Collector<T, A, R>的一个实现类,为了自定义我们自己的Collector, ...

  6. java 月份适配计算_Java8中的时间日期API这么好用,你居然还没有掌握?

    工作这么久了,对于Java中时间日期的操作一直很蛋疼,一会用Date,一会用Calendar或者LocalDateTime,始终没有认真总结过它们的联系与区别.迷迷糊糊用了好几年了,今天终于搞清楚了! ...

  7. java酷炫代码_Java8 中有趣酷炫的小技巧

    执行注释 大多数开发人员认为注释永远不会在程序中执行,并用于帮助代码理解.但是,它们却可以被执行: public class Main { public static void main(String ...

  8. java optional详解_java8中Optional的用法详解

    package com.lyzx.concurrent.java8; import org.junit.Test; import java.util.Optional; import java.uti ...

  9. java 8 stream 性能_java8中parallelStream性能测试及结果分析

    测试1 @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 5, t ...

最新文章

  1. Logger对象父子关系
  2. 一个DDOS病毒的分析(一)
  3. qq邮箱使用outlook 2007
  4. 满意度调查access模板_洪安镇推进综合便民服务中心标准化建设,着力提升群众满意度...
  5. 【渝粤教育】电大中专常见病药物治疗作业 题库
  6. mui实现分享功能_MUI 分享功能(微信、QQ 、朋友圈)
  7. django-模型类字段类型
  8. Pandas一些小技巧
  9. ARM 指令集版本和ARM 版本z
  10. 专业的Windows鼠标右键菜单管理工具
  11. 使用树莓派打造家庭监控系统
  12. 华为HMS:风雨突然,仍求自我
  13. 标签类目体系(面向业务的数据资产设计方法论)-读书笔记3
  14. Oracle Database 10g for Windows安装
  15. 解决pip下载速度慢的问题:更换国内源
  16. html里列表前的空心圆圈,如何在HTML中创建带有圆圈项目符号的无序列表?
  17. 数字图像处理第九章——形态学图像处理
  18. iphonex桌面的计算机不见了,iPhone 桌面上找不到应用图标了怎么办?
  19. 中国硫酸铜杀菌剂市场趋势报告、技术动态创新及市场预测
  20. 武姓女孩五行缺金取名

热门文章

  1. python中枚举类型enum用法_Python枚举类型Enum用法详解
  2. android 圆形相机预览拍照_Android多种方式实现相机圆形预览
  3. 基于Vue与Cytoscape实现企业控股关系图
  4. 一直以来伴随我的一些学习习惯(三):阅读方法
  5. Android通讯录管理(获取联系人 通话记录 短信消息)(一)
  6. 机房供电系统设计——机房常用供电方式
  7. ORACLE通配符转义
  8. 【基础网络】TCP与UDP 的区别
  9. pdf如何压缩大小?pdf文件太大了怎么变小?
  10. 计算机视觉(八):图像分割