前言

Java8提供的Stream接口使流式编程和函数式编程更加容易。

现在一些集合的处理,经常会使用Stream来进行处理,相比循环,代码的可读性有所提高。如果更进一步,再利用上Reactor进行反应式编程,则会带来更多优势,如异常处理、执行线程控制、并行、缓冲等,声明式的完成了许多命令式编程许多代码才能完成的功能。

场景

一次使用Stream进行收集的过程中,同时使用Collectors.groupingByCollectors.reducing,出现了问题。这里记录一下。

场景是一批业务对象Foo

@Getter
@Setter
@ToString
class Foo{// 分组属性private String name;// 需求:分组平均值private Integer m;// 需求:分组平均值private Integer n;// 非业务属性,计算平均值时使用private Integer count;public Foo(String name, Integer m, Integer n) {this.name = name;this.m = m;this.n = n;}
}

笔者想根据Stream<Foo>对这个流先按照name分组收集,再每组统计m,n属性的平均值,最终仍返回一个Foo,便类似下面这样写了出来:

Foo f1 = new Foo("l", 1, 2),f2 = new Foo("l", 1, 2),f3 = new Foo("l", 1, 2),f4 = new Foo("c", 1, 2),f5 = new Foo("c", 1, 2),f6 = new Foo("q", 1, 2),f7 = new Foo("q", 1, 2);
System.out.println(Stream.of(f1, f2, f3, f4, f5, f6, f7).collect(Collectors.groupingBy(Foo::getName,Collectors.reducing(new Foo(null, 0, 0), (o, p) -> {if (o.getName() == null) {o.setName(p.getName());}o.setM(o.getM() + p.getM());o.setN(o.getN() + p.getN());o.setCount(o.getCount() == null ? 1 : o.getCount() + 1);return o;})))
);

最后发现结果为:

{q=Foo(name=l, m=7, n=14, count=6), c=Foo(name=l, m=7, n=14, count=6), l=Foo(name=l, m=7, n=14, count=6)}

按照名称分组后各组的Foo都是一个,代码并未向预想那样,先groupingBy分组,再按照每组进行reducing收集,而是把原始Stream全收集为一个对象给了每组作为值。

原因和解决方案

查了查StackOverflow,看了下Collectors.reducing的源码:

public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op) {return new CollectorImpl<>(// 作为accumulator的函数式接口,看下段boxSupplier(identity),(a, t) -> { a[0] = op.apply(a[0], t); },(a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },a -> a[0],CH_NOID);
}// 重点,每次只返回传进来的对象,而不是新创建一个
private static <T> Supplier<T[]> boxSupplier(T identity) {return () -> (T[]) new Object[] { identity };
}

发现了,Collectors.reducing 所创建的 accumulator 返回的是一个传进来的固定对象,不会因为上游是Collectors.groupingBy就新创建然后分组,所以最好不要修改Collectors.reducing的参数

最新的需求可以这样解决:

System.out.println(Stream.of(f1, f2, f3, f4, f5, f6, f7).collect(Collectors.groupingBy(Foo::getName,// 新构建Collector,每组accumulator会新创建FooCollector.of(() -> new Foo(null, 0, 0), (o, p) -> {if (o.getName() == null) {o.setName(p.getName());}o.setM(o.getM() + p.getM());o.setN(o.getN() + p.getN());o.setCount(o.getCount() == null ? 1 : o.getCount() + 1);}, (o, p) -> o)))
);

输出了想要的结果:

{q=Foo(name=q, m=2, n=4, count=2), c=Foo(name=c, m=2, n=4, count=2), l=Foo(name=l, m=3, n=6, count=3)}

参考

https://stackoverflow.com/questions/42981783/java-8-stream-collect-groupingby-mapping-reducing-reducing-b

JDK11.0.2

`Stream`的`Collectors.reducing`与`Collectors.groupingBy`相关推荐

  1. Collectors.reducing总结Collectors.mapping+Collectors.reducing+TreeSet等等

    Collectors.reducing总结 1. 方法签名 一个参数 public static <T> Collector<T, ?, Optional<T>> ...

  2. java实现两个时间的累加_java List 相邻两个数据累加,可以用stream的collectors.reducing实现么...

    答案2.0 害,之前上一版答案,由于需求没有理解正确,所以惨败,这次再次理清了哈需求,这哪是List合并成一个LineDto 这里不得不说哈题主(甩锅开始),非要说用collectors.reduci ...

  3. Java造两个list_java List 相邻两个数据累加,可以用stream的collectors.reducing实现么

    答案2.0 害,之前上一版答案,由于需求没有理解正确,所以惨败,这次再次理清了哈需求,这哪是List合并成一个LineDto 这里不得不说哈题主(甩锅开始),非要说用collectors.reduci ...

  4. java8 stream().map().collect()的Collectors.toList()、Collectors.toMap()、Collectors.groupingBy()的

    一.Collectors.toList() 现在有个集合: List<User> users = getUserList(); 现在需要将这些user的id提取出来.这个很简单,for循环 ...

  5. java8之stream流之Collector和Collectors

    Collector Collector是专门用来作为Stream的collect方法的参数的. public interface Stream<T> extends BaseStream& ...

  6. Stream流与Lambda表达式(三) 静态工厂类Collectors

    /*** @author 陈杨*/@SpringBootTest @RunWith(SpringRunner.class) public class CollectorsDetail {private ...

  7. 用流收集数据Collectors的用法介绍分组groupingBy、分区partitioningBy(一)

    文章目录 一.收集器简介 二.归约和汇总 1.查找流中最大值和最小值Collectors.maxBy和,Collectors.minBy 2.汇总 3.连接字符串 4.广义归约汇总 三.分组 1.多级 ...

  8. java8 Lambda Stream collect Collectors 常用实例

    将一个对象的集合转化成另一个对象的集合 List<OrderDetail> orderDetailList = orderDetailService.listOrderDetails(); ...

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

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

  10. Stream操作时Collectors工具类中常用方法

    文章目录 示例文件准备 实体类User : 测试main方法制作数据 : 聚合与分组 toList.toSet.toCollection toMap.toConcurrentMap groupingB ...

最新文章

  1. 问题1:U盘可以识别但无法打开;问题2:U盘成为启动盘之后如何恢复成普通U盘。
  2. postman测试传入json
  3. 华为云计算之FusionStorage
  4. 【leetcode】521. Longest Uncommon Subsequence I
  5. 全网最新Spring Boot2.5.1整合Activiti5.22.0企业实战教程<基础篇>
  6. Phoenix官方教程 (一) 构建和运行
  7. 音乐计算机官方.,Boom音乐电脑版
  8. WinForm(一) WinForm入门与基本控件使用
  9. 电子商务网站设计中信息安全防御
  10. Matlab龚珀兹曲线模型预测,指数曲线模型的讲解=.pptx
  11. 编写java的软件——Myeclipse,以及反编译的用法.
  12. vue移动端下拉刷新、上拉加载
  13. 腾讯云SDK使用python版
  14. 安卓版炉石修改服务器,越南玩家自制炉石传说安卓版客户端 可能公开移植方法...
  15. 学习方法——TRIZ创新理论中的40个发明原则(四)
  16. 漏洞评估-CVSS3
  17. 安卓开发 切换应用语言和获取系统语言
  18. php 验证码的背景色,做验证码时ImageCreatetruecolor背景颜色只显黑色的解决办法
  19. 润乾报表改java路径_Mac 环境中部署报表
  20. 软件测试周刊(第77期):只要放弃一次,就会滋生放弃的习性, 原本可以解决的问题也会变得无法解决。

热门文章

  1. OpenCV-图像翻转函数cv::filp
  2. vue.jsv-html,关于vue.jsv-bind的一些理解和思考.pdf
  3. 微信企业号开发:openapi回调地址请求不通过
  4. 十六进制字符串转中文
  5. 树莓派Pico开发软件安装(Thonny)及烧录(flash)
  6. 农夫山泉推出新品矿泉水“长白雪”域名表现如何?
  7. selectpicker的使用方法
  8. 计算英文句子中有多少单词?
  9. 银行手机APP软件做性能测试吗,浅谈银行开放平台应用系统性能测试
  10. java计算机毕业设计公立医院绩效考核系统源码+mysql数据库+系统+lw文档+部署