如何通过单个Java Stream按国家分组统计正负金额总和?
单个Stream实现按国家分组统计正负金额总和
当然可以用单个Stream搞定这个需求!核心思路是利用Collectors.groupingBy结合自定义的下游收集器,在分组的同时一次性计算每个国家的正金额总和和负金额总和。
步骤拆解与代码实现
首先先确认你的Sale类和测试数据(补全基础结构):
class Sale { private String country; private BigDecimal amount; public Sale(String country, BigDecimal amount) { this.country = country; this.amount = amount; } public String getCountry() { return country; } public BigDecimal getAmount() { return amount; } } // 测试数据 List<Sale> sales = Arrays.asList( new Sale("nl", new BigDecimal("9")), new Sale("nl", new BigDecimal("-3")), new Sale("be", new BigDecimal("7.9")), new Sale("be", new BigDecimal("-7")) );
接下来直接上单个Stream的实现代码(这里用Java标准库的AbstractMap.SimpleEntry替代Pair,如果你有自定义Pair类直接替换即可):
Map<String, AbstractMap.SimpleEntry<BigDecimal, BigDecimal>> result = sales.stream() .collect(Collectors.groupingBy( Sale::getCountry, Collectors.reducing( // 初始值:正金额总和、负金额总和都设为0 new AbstractMap.SimpleEntry<>(BigDecimal.ZERO, BigDecimal.ZERO), // 单个Sale的转换逻辑:判断金额正负,对应放到正/负总和的位置 sale -> { BigDecimal amount = sale.getAmount(); if (amount.compareTo(BigDecimal.ZERO) > 0) { return new AbstractMap.SimpleEntry<>(amount, BigDecimal.ZERO); } else { return new AbstractMap.SimpleEntry<>(BigDecimal.ZERO, amount); } }, // 合并逻辑:把两个Entry的正总和、负总和分别相加 (entry1, entry2) -> new AbstractMap.SimpleEntry<>( entry1.getKey().add(entry2.getKey()), entry1.getValue().add(entry2.getValue()) ) ) ));
代码细节解释
- 分组核心:
Collectors.groupingBy(Sale::getCountry)负责按国家把所有Sale对象归类到对应的分组下。 - 下游收集器
reducing:- 第一个参数是初始状态:每个分组的正、负金额总和都从0开始计算。
- 第二个参数是单个元素的转换:把每个Sale转换成只包含当前金额(正金额放到key,负金额放到value)的Entry,方便后续累加。
- 第三个参数是分组内的合并规则:把分组里所有Entry的正金额、负金额分别累加,得到最终的统计结果。
如果你的项目里有自定义的Pair<BigDecimal, BigDecimal>类,只需要把AbstractMap.SimpleEntry替换成你的Pair即可,比如:
Map<String, Pair<BigDecimal, BigDecimal>> result = sales.stream() .collect(Collectors.groupingBy( Sale::getCountry, Collectors.reducing( new Pair<>(BigDecimal.ZERO, BigDecimal.ZERO), sale -> { BigDecimal amount = sale.getAmount(); return amount.compareTo(BigDecimal.ZERO) > 0 ? new Pair<>(amount, BigDecimal.ZERO) : new Pair<>(BigDecimal.ZERO, amount); }, (p1, p2) -> new Pair<>(p1.getFirst().add(p2.getFirst()), p1.getSecond().add(p2.getSecond())) ) ));
最终结果验证
运行后得到的Map会是:
nl对应的Pair:正总和9,负总和-3be对应的Pair:正总和7.9,负总和-7
完全符合你要的分组统计需求,而且全程只用了一个Stream!
内容的提问来源于stack exchange,提问作者remmelt
相关产品推荐
相关产品推荐

