You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过单个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())
            )
        )
    ));

代码细节解释

  1. 分组核心:Collectors.groupingBy(Sale::getCountry)负责按国家把所有Sale对象归类到对应的分组下。
  2. 下游收集器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,负总和-3
  • be对应的Pair:正总和7.9,负总和-7

完全符合你要的分组统计需求,而且全程只用了一个Stream!

内容的提问来源于stack exchange,提问作者remmelt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 09:18:09