前言

随着微服务的发展,越来越多的sql处理被放到java来处理,数据库经常会使用到对集合中的数据进行分组求和,分组运算等等。
那怎么样使用java的stream优雅的进行分组求和或运算呢?

一、准备测试数据

这里测试数据学生,年龄类型是Integer,身高类型是BigDecimal,我们分别对身高个年龄进行求和。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {/*** 姓名*/private String name;/*** 年龄*/private Integer age;/*** 身高*/private BigDecimal stature;
}public class LambdaLearn {// 初始化的测试数据集合static List<Student> list = new ArrayList<>();static {// 初始化测试数据list.add(new Student("张三", 18, new BigDecimal("185")));list.add(new Student("张三", 19, new BigDecimal("185")));list.add(new Student("张三2", 20, new BigDecimal("180")));list.add(new Student("张三3", 20, new BigDecimal("170")));list.add(new Student("张三3", 21, new BigDecimal("172")));}
}

二、按学生姓名分组求年龄和(Integer类型的求和简单示例)

1.实现

// 按学生姓名分组求年龄和
public static void main(String[] args) {Map<String, Integer> ageGroup = list.stream().collect(Collectors.groupingBy(Student::getName, Collectors.summingInt(Student::getAge)));System.out.println(ageGroup);
}
执行结果:
{张三=37, 张三3=41, 张三2=20}

三、按学生姓名分组求身高和(Collectors没有封装对应的API)

1.实现一(推荐写法)

思路:先分组,再map转换成身高BigDecimal,再用reduce进行求和

public static void main(String[] args) {Map<String, BigDecimal> ageGroup = list.stream().collect(Collectors.groupingBy(Student::getName, Collectors.mapping(Student::getStature, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));System.out.println(ageGroup);
}
执行结果:
{张三=370, 张三3=342, 张三2=180}

2.实现二

思路:先分组,再收集成list,然后再map,再求和

public static void main(String[] args) {Map<String, BigDecimal> ageGroup = list.stream().collect(Collectors.groupingBy(Student::getName, Collectors.collectingAndThen(Collectors.toList(), x -> x.stream().map(Student::getStature).reduce(BigDecimal.ZERO, BigDecimal::add))));System.out.println(ageGroup);
}
执行结果:
{张三=370, 张三3=342, 张三2=180}

3.实现三

思路:业务时常在分组后需要做一些判断逻辑再进行累加业务计算,所以自己实现一个收集器

1.封装一个自定义收集器

public class MyCollector {static final Set<Collector.Characteristics> CH_CONCURRENT_ID= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,Collector.Characteristics.UNORDERED,Collector.Characteristics.IDENTITY_FINISH));static final Set<Collector.Characteristics> CH_CONCURRENT_NOID= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,Collector.Characteristics.UNORDERED));static final Set<Collector.Characteristics> CH_ID= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));static final Set<Collector.Characteristics> CH_UNORDERED_ID= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED,Collector.Characteristics.IDENTITY_FINISH));static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();private MyCollector() {}@SuppressWarnings("unchecked")private static <I, R> Function<I, R> castingIdentity() {return i -> (R) i;}/*** @param <T> 集合元素类型* @param <A> 中间结果容器* @param <R> 最终结果类型*/static class CollectorImpl<T, A, R> implements Collector<T, A, R> {private final Supplier<A> supplier;private final BiConsumer<A, T> accumulator;private final BinaryOperator<A> combiner;private final Function<A, R> finisher;private final Set<Characteristics> characteristics;CollectorImpl(Supplier<A> supplier,BiConsumer<A, T> accumulator,BinaryOperator<A> combiner,Function<A, R> finisher,Set<Characteristics> characteristics) {this.supplier = supplier;this.accumulator = accumulator;this.combiner = combiner;this.finisher = finisher;this.characteristics = characteristics;}CollectorImpl(Supplier<A> supplier,  // 产生结果容器BiConsumer<A, T> accumulator,  // 累加器BinaryOperator<A> combiner, // 将多个容器结果合并成一个Set<Characteristics> characteristics) {this(supplier, accumulator, combiner, castingIdentity(), characteristics);}@Overridepublic BiConsumer<A, T> accumulator() {return accumulator;}@Overridepublic Supplier<A> supplier() {return supplier;}@Overridepublic BinaryOperator<A> combiner() {return combiner;}@Overridepublic Function<A, R> finisher() {return finisher;}@Overridepublic Set<Characteristics> characteristics() {return characteristics;}}public static <T> Collector<T, ?, BigDecimal> summingDecimal(ToDecimalFunction<? super T> mapper) {return new MyCollector.CollectorImpl<>(() -> new BigDecimal[1],(a, t) -> {if (a[0] == null) {a[0] = BigDecimal.ZERO;}a[0] = a[0].add(Optional.ofNullable(mapper.applyAsDecimal(t)).orElse(BigDecimal.ZERO));},(a, b) -> {a[0] = a[0].add(Optional.ofNullable(b[0]).orElse(BigDecimal.ZERO));return a;},a -> a[0], CH_NOID);}}

2.封装一个函数式接口

@FunctionalInterface
public interface ToDecimalFunction<T> {BigDecimal applyAsDecimal(T value);
}

3.使用

public static void main(String[] args) {Map<String, BigDecimal> ageGroup = list.stream().collect(Collectors.groupingBy(Student::getName, MyCollector.summingDecimal(Student::getStature)));System.out.println(ageGroup);
}

总结

自定义实现收集器可以参考Collectors的内部类CollectorImpl的源码,具体解析写到注释中。
推荐通过模仿Collectors.summingInt()的实现来实现我们自己的收集器。

// T代表流中元素的类型,A是中间处理临时保存类型,R代表返回结果的类型
static class CollectorImpl<T, A, R> implements Collector<T, A, R> {private final Supplier<A> supplier;private final BiConsumer<A, T> accumulator;private final BinaryOperator<A> combiner;private final Function<A, R> finisher;private final Set<Characteristics> characteristics;CollectorImpl(Supplier<A> supplier,BiConsumer<A, T> accumulator,BinaryOperator<A> combiner,Function<A,R> finisher,Set<Characteristics> characteristics) {this.supplier = supplier;this.accumulator = accumulator;this.combiner = combiner;this.finisher = finisher;this.characteristics = characteristics;}CollectorImpl(Supplier<A> supplier,BiConsumer<A, T> accumulator,BinaryOperator<A> combiner,Set<Characteristics> characteristics) {this(supplier, accumulator, combiner, castingIdentity(), characteristics);}// 这里提供一个初始化的容器,用于存储每次累加。即使我们求和这里也只能使用容器存储,否则后续计算累加结果会丢失(累加结果不是通过返回值方式修改的)。@Overridepublic Supplier<A> supplier() {return supplier;}// 累加计算:累加流中的每一个元素T到A容器存储的结果中,这里没有返回值,所以A必须是容器,避免数据丢失@Overridepublic BiConsumer<A, T> accumulator() {return accumulator;}// 这里是当开启parallelStream()并发处理时,会得到多个结果容器A,这里对多个结果进行合并@Overridepublic BinaryOperator<A> combiner() {return combiner;}// 这里是处理中间结果类型转换成返回结果类型@Overridepublic Function<A, R> finisher() {return finisher;}// 这里标记返回结果的数据类型,这里取值来自于Collector接口的内部类Characteristics@Overridepublic Set<Characteristics> characteristics() {return characteristics;}}
enum Characteristics {// 表示此收集器是 并发的 ,这意味着结果容器可以支持与多个线程相同的结果容器同时调用的累加器函数。 CONCURRENT,// 表示收集操作不承诺保留输入元素的遇到顺序。UNORDERED,// 表示整理器功能是身份功能,可以被删除。 IDENTITY_FINISH
}

java stream实现分组BigDecimal求和,自定义分组求和相关推荐

  1. 使用Java Stream API将List按自定义分组规则转换成Map的一个例子

    本文完整测试代码见文末. 测试数据是List里的4个员工对象实例: 根据员工所在的城市进行分组: 结果分成了三组: 第一组的员工在上海: 第二组的员工在成都: 统计每组员工个数: 把员工进行分组,得分 ...

  2. java list 自定义类型转换_使用Java Stream API将List按自定义分组规则转换成Map的一个例子...

    本文完整测试代码见文末. 测试数据是List里的4个员工对象实例: 根据员工所在的城市进行分组: 结果分成了三组: 第一组的员工在上海: 第二组的员工在成都: 统计每组员工个数: 把员工进行分组,得分 ...

  3. R语言使用ggplot2包使用geom_violin函数绘制分组小提琴图(自定义分组的填充色)实战

    R语言使用ggplot2包使用geom_violin函数绘制分组小提琴图(自定义分组的填充色)实战 目录

  4. R语言使用ggplot2包使用geom_violin函数绘制分组小提琴图(自定义分组的次序)实战

    R语言使用ggplot2包使用geom_violin函数绘制分组小提琴图(自定义分组的次序)实战 目录

  5. R语言使用ggplot2包使用geom_violin函数绘制分组小提琴图(自定义分组的颜色)实战

    R语言使用ggplot2包使用geom_violin函数绘制分组小提琴图(自定义分组的颜色)实战 目录

  6. Java Stream 实用特性:排序、分组和 teeing

    排序 基本数据类型排序 基本数据类型就是字符串.整型.浮点型这些,也就是要排序的列表中的元素都是这些基本类型的,比如 List<Integer>的. 下面就用一个整型列表举例说明. 正序排 ...

  7. java steam 排序_java8 stream自定义分组求和并排序的实现

    本文主要介绍了java8 stream自定义分组求和并排序的实现,分享给大家,具体如下: public static void main(String[] args) { List list = ne ...

  8. java stream 分组求和_Java stream List 求和、分组操作

    Java stream List 求和.分组操作 前言 项目中经常会使用Stream操作一些集合数据,今天记录一下我经常使用的Stream操作 求和操作public static void main( ...

  9. Java stream 处理分组后[升降序key]取每组最大

    Java stream 处理分组后[升降序key]取每组最大 一. 需求 二. 整活 git仓库直达 如何让同事看不懂你写的代码 然后觉得你非常牛逼 这里用到了stream()与Lambda 需要有点 ...

最新文章

  1. cron 12点执行_Linux中得循环调度任务执行
  2. 某同学工作之后的感悟
  3. 【ES6(2015)】新的声明方式 let、const
  4. py脚本实现用例执行html报告,pytest文档7-pytest-html生成html报告
  5. Parse Server(含Dashboard)部署于Centos7.6 64位
  6. 【采集读写】基于matlab语音采集与读写【含Matlab源码 538期】
  7. (转)CocosCreator零基础制作游戏《极限跳跃》五、制作游戏主场景背景滚动
  8. 在线解方程软件集合(收藏)
  9. wps小写金额转大写快捷键_日记坊 - excel小写金额快速转换大写的几个小技巧
  10. 蛋白组学搜库分析软件 MaxQuant使用教程
  11. Java对象的GC内存分配和回收策略
  12. 计算机专业英语常用词汇整理
  13. 技术接受模型(TAM,Technology Acceptance Model)
  14. 支持小米java文件阅读器_小米多看电纸书MiReader 桌面LauncherApp(自制软件)
  15. 16 个百度网盘搜索引擎
  16. 6月楼市或迎降价潮 房企策略有调整
  17. mybatis一级缓存、二级缓存以及集成EnCache、Redis,避免脏读
  18. win10:Python3.7安装PyQt4
  19. 图片验证码不显示解决方案
  20. 目标检测 SSD: Single Shot MultiBox Detector - SSD在MMDetection中的实现

热门文章

  1. 毕业论文格式、画图(仅限matlab)攻略
  2. vue 使用 iconfont-阿里巴巴矢量图标库
  3. JS小游戏(坦克大战)
  4. 信息学奥赛-逻辑运算
  5. 简约计算器效果 html+css
  6. Webstorm自动保存功能设置
  7. Axure 引入元件库
  8. (王道408考研数据结构)第三章栈和队列-第五节:Java、C++、Python实现栈和队列
  9. apt-get软件管理工具(软件安装、重装、卸载)
  10. Linux多线程 | 线程同步