如何在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。
原有分步实现
原方案需分三步完成逻辑:
- 生成取款交易流:
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() ));
- 生成存款交易流:
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() ));
- 合并流并计算最终余额:
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
相关产品推荐
相关产品推荐

