如何使用Java Stream将多场所货币金额转换为聚合Map<Currency, BigDecimal>
用Java Stream实现多场所货币金额汇总
这问题我熟,用Java Stream来做其实很直观,核心就是两步:把所有工作场所的货币-金额数据扁平化,再按货币类型分组求和。下面给你拆解具体实现,附上可运行的代码示例:
第一步:定义工作场所模型
首先我们需要一个Workplace类,用来表示单个工作场所的货币金额数据:
import java.math.BigDecimal; import java.util.Currency; import java.util.Map; public class Workplace { private final Map<Currency, BigDecimal> currencyAmounts; public Workplace(Map<Currency, BigDecimal> currencyAmounts) { this.currencyAmounts = currencyAmounts; } public Map<Currency, BigDecimal> getCurrencyAmounts() { return currencyAmounts; } }
第二步:核心Stream汇总逻辑
假设你已经有了一个包含所有工作场所的List<Workplace>,我们可以用Stream的flatMap和分组收集器来完成汇总:
import java.math.BigDecimal; import java.util.Currency; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class CurrencySumDemo { public static void main(String[] args) { // 模拟测试数据:两个工作场所的货币金额 Workplace shop1 = new Workplace(Map.of( Currency.getInstance("USD"), new BigDecimal("20"), Currency.getInstance("EUR"), new BigDecimal("40") )); Workplace shop2 = new Workplace(Map.of( Currency.getInstance("USD"), new BigDecimal("50"), Currency.getInstance("EUR"), new BigDecimal("10") )); List<Workplace> allWorkplaces = List.of(shop1, shop2); // 核心汇总代码 Map<Currency, BigDecimal> totalByCurrency = allWorkplaces.stream() // 1. 扁平化:把每个场所的货币Map拆成单个Entry流 .flatMap(workplace -> workplace.getCurrencyAmounts().entrySet().stream()) // 2. 分组求和:按货币类型分组,对同货币的金额累加 .collect(Collectors.groupingBy( Map.Entry::getKey, // 分组键:货币类型 Collectors.reducing( BigDecimal.ZERO, // 累加初始值(避免空指针) Map.Entry::getValue, // 提取要累加的金额 BigDecimal::add // 累加操作 ) )); // 打印结果验证 totalByCurrency.forEach((currency, total) -> System.out.printf("%s: %s%n", currency.getCurrencyCode(), total)); // 输出结果: // USD: 70 // EUR: 50 } }
关键步骤解释
- 扁平化处理:
flatMap的作用是把每个工作场所的Map<Currency, BigDecimal>转换成Stream<Map.Entry<Currency, BigDecimal>>,这样所有场所的货币-金额对都变成了流中的独立元素,方便后续统一分组。 - 分组求和:
Collectors.groupingBy负责按货币类型分组,下游收集器Collectors.reducing则完成同一组内的金额累加:BigDecimal.ZERO作为初始值,确保即使某个货币没有任何金额数据时,也能返回0(增强代码健壮性);Map.Entry::getValue用来提取每个Entry中的金额值;BigDecimal::add是BigDecimal的累加方法,因为BigDecimal是不可变类,这个操作会返回新的BigDecimal实例,线程安全。
Java 12+ 简化写法
如果你使用的是Java 12及以上版本,可以用Collectors.summingBigDecimal来替代reducing,代码会更简洁:
.collect(Collectors.groupingBy( Map.Entry::getKey, Collectors.summingBigDecimal(Map.Entry::getValue) ));
注意事项
Currency是单例类,同一个货币代码(比如"USD")的Currency实例是同一个对象,所以作为Map的键不会有问题;- 确保所有金额都是
BigDecimal类型(不要用double,避免精度丢失),这和你的需求完全匹配。
内容的提问来源于stack exchange,提问作者Артем Потешкин
相关产品推荐
相关产品推荐

