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

如何在Java Stream中合并两个groupingBy操作?

单次流操作实现交易金额汇总逻辑

输入输出对象定义

输入对象Txn:

@Getter
class Txn {
    private String hash;
    private String withdrawId;
    private String depositId;
    private Integer amount;
    private String date;
}

输出对象UserTxn:

@Builder
@Getter
class UserTxn {
    private String hash;
    private String walletId;
    private String txnType;
    private Integer amount;
}

需求说明

Txn代表从withdrawId账户转出金额至depositId账户的交易。需按交易hash分组后,对每个关联钱包账户(转出/转入方)计算最终余额:转入总金额 - 转出总金额,最终生成UserTxn对象——余额为正标记DEPOSIT,为负标记WITHDRAW。

原有分步实现

原方案需分三步完成逻辑:

  1. 生成取款交易流:
var withdrawStream = txnList.stream().collect(Collectors.groupingBy(Txn::getHash, LinkedHashMap::new,
        Collectors.groupingBy(Txn::getWithdrawId, LinkedHashMap::new, Collectors.toList())))
    .entrySet().stream().flatMap(hashEntrySet -> hashEntrySet.getValue().entrySet().stream()
        .map(withdrawEntrySet ->
            UserTxn.builder()
                .hash(hashEntrySet.getKey())
                .walletId(withdrawEntrySet.getKey())
                .txnType("WITHDRAW")
                .amount(withdrawEntrySet.getValue().stream().map(Txn::getAmount).reduce(0, Integer::sum))
                .build()
        ));
  1. 生成存款交易流:
var depositStream = txnList.stream().collect(Collectors.groupingBy(Txn::getHash, LinkedHashMap::new,
        Collectors.groupingBy(Txn::getDepositId, LinkedHashMap::new, Collectors.toList())))
    .entrySet().stream().flatMap(hashEntrySet -> hashEntrySet.getValue().entrySet().stream()
        .map(withdrawEntrySet ->
            UserTxn.builder()
                .hash(hashEntrySet.getKey())
                .walletId(withdrawEntrySet.getKey())
                .txnType("DEPOSIT")
                .amount(withdrawEntrySet.getValue().stream().map(Txn::getAmount).reduce(0, Integer::sum))
                .build()
        ));
  1. 合并流并计算最终余额:
var res = Stream.concat(withdrawStream, depositStream).collect(Collectors.groupingBy(UserTxn::getHash, LinkedHashMap::new,
    Collectors.groupingBy(UserTxn::getWalletId, LinkedHashMap::new, Collectors.toList())))
    .entrySet().stream().flatMap(hashEntrySet -> hashEntrySet.getValue().entrySet().stream()
        .map(withdrawEntrySet -> {
              var depositAmount = withdrawEntrySet.getValue().stream().filter(userTxn -> userTxn.getTxnType().equals("DEPOSIT")).map(UserTxn::getAmount).reduce(0, Integer::sum);
              var withdrawAmount = withdrawEntrySet.getValue().stream().filter(userTxn -> userTxn.getTxnType().equals("WITHDRAW")).map(UserTxn::getAmount).reduce(0, Integer::sum);
              var totalAmount = depositAmount - withdrawAmount;
              return UserTxn.builder()
                  .hash(hashEntrySet.getKey())
                  .walletId(withdrawEntrySet.getKey())
                  .txnType(totalAmount > 0 ? "DEPOSIT" : "WITHDRAW")
                  .amount(totalAmount)
                  .build();
            }
        ));

单次流操作优化实现

核心思路:将每个Txn拆分为两条记录——转出方(withdrawId对应负金额)、转入方(depositId对应正金额),直接按hash+walletId分组汇总金额,最后转换为UserTxn对象。

完整代码:

var res = txnList.stream()
        // 拆分每个交易为转出、转入两条金额记录
        .flatMap(txn -> Stream.of(
                new AbstractMap.SimpleEntry<>(
                        new AbstractMap.SimpleEntry<>(txn.getHash(), txn.getWithdrawId()),
                        -txn.getAmount()
                ),
                new AbstractMap.SimpleEntry<>(
                        new AbstractMap.SimpleEntry<>(txn.getHash(), txn.getDepositId()),
                        txn.getAmount()
                )
        ))
        // 按hash+walletId分组,汇总金额
        .collect(Collectors.groupingBy(
                AbstractMap.SimpleEntry::getKey,
                LinkedHashMap::new,
                Collectors.summingInt(AbstractMap.SimpleEntry::getValue)
        ))
        .entrySet().stream()
        // 转换为UserTxn对象
        .map(entry -> {
            var hashWalletPair = entry.getKey();
            var totalAmount = entry.getValue();
            return UserTxn.builder()
                    .hash(hashWalletPair.getKey())
                    .walletId(hashWalletPair.getValue())
                    .txnType(totalAmount > 0 ? "DEPOSIT" : "WITHDRAW")
                    .amount(totalAmount)
                    .build();
        });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 18:10:48