文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

java stream实现分组BigDecimal求和,自定义分组求和

2023-08-16 20:30

关注

前言

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

一、准备测试数据

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

@Data@AllArgsConstructor@NoArgsConstructorpublic class Student {        private String name;        private Integer age;        private BigDecimal stature;}public class LambdaLearn {// 初始化的测试数据集合    static List 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 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 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 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 CH_CONCURRENT_ID            = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,            Collector.Characteristics.UNORDERED,            Collector.Characteristics.IDENTITY_FINISH));    static final Set CH_CONCURRENT_NOID            = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,            Collector.Characteristics.UNORDERED));    static final Set CH_ID            = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));    static final Set CH_UNORDERED_ID            = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED,            Collector.Characteristics.IDENTITY_FINISH));    static final Set CH_NOID = Collections.emptySet();    private MyCollector() {    }    @SuppressWarnings("unchecked")    private static  Function castingIdentity() {        return i -> (R) i;    }        static class CollectorImpl implements Collector {        private final Supplier supplier;        private final BiConsumer accumulator;        private final BinaryOperator combiner;        private final Function finisher;        private final Set characteristics;        CollectorImpl(Supplier supplier,                      BiConsumer accumulator,                      BinaryOperator combiner,                      Function finisher,                      Set characteristics) {            this.supplier = supplier;            this.accumulator = accumulator;            this.combiner = combiner;            this.finisher = finisher;            this.characteristics = characteristics;        }        CollectorImpl(Supplier supplier,  // 产生结果容器                      BiConsumer accumulator,  // 累加器                      BinaryOperator combiner, // 将多个容器结果合并成一个                      Set characteristics) {            this(supplier, accumulator, combiner, castingIdentity(), characteristics);        }        @Override        public BiConsumer accumulator() {            return accumulator;        }        @Override        public Supplier supplier() {            return supplier;        }        @Override        public BinaryOperator combiner() {            return combiner;        }        @Override        public Function finisher() {            return finisher;        }        @Override        public Set characteristics() {            return characteristics;        }    }    public static  Collector summingDecimal(ToDecimalFunction 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.封装一个函数式接口

@FunctionalInterfacepublic interface ToDecimalFunction {    BigDecimal applyAsDecimal(T value);}

3.使用

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

来源地址:https://blog.csdn.net/weixin_38387358/article/details/129003917

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯