Java使用Stream按Product的vat分组后统计各数值字段总和如何实现
解决方案
你可以通过自定义统计结果实体+Stream分组收集的方式实现需求,有两种常用实现方式如下:
第一步:定义统计结果实体
先新建TaxSummary类用来存储每个vat税率对应的三个字段总和:
public class TaxSummary { private double totalNetPrice; private double totalVatAmount; private double totalGrossPrice; // 累加单个商品的金额 public void addProduct(Product product) { this.totalNetPrice += product.getNetPrice(); this.totalVatAmount += product.getVatAmount(); this.totalGrossPrice += product.getGrossPrice(); } // 合并两个统计结果(并行流场景会用到) public TaxSummary merge(TaxSummary other) { this.totalNetPrice += other.totalNetPrice; this.totalVatAmount += other.totalVatAmount; this.totalGrossPrice += other.totalGrossPrice; return this; } @Override public String toString() { return "TaxSummary{" + "totalNetPrice=" + totalNetPrice + ", totalVatAmount=" + totalVatAmount + ", totalGrossPrice=" + totalGrossPrice + '}'; } // 按需添加getter方法 }
第二步:修改业务方法
实现方式1:先分组再遍历统计(兼容性最好,Java 8+全版本支持,逻辑直观)
如果需要保留按vat分组后的Product列表,用这种方式:
public static Map<Double, TaxSummary> calculatingTaxes(List<Product> products){ // 原有的按vat分组逻辑 Map<Double, List<Product>> productTaxesMap = products.stream() .collect(Collectors.groupingBy(Product::getVat)); // 遍历每个分组计算总和 Map<Double, TaxSummary> summaryMap = new HashMap<>(); productTaxesMap.forEach((vat, productList) -> { TaxSummary summary = new TaxSummary(); productList.forEach(summary::addProduct); summaryMap.put(vat, summary); }); // 测试输出结果 summaryMap.forEach((vat, summary) -> System.out.printf("税率%.2f 统计结果:%s%n", vat, summary)); return summaryMap; }
实现方式2:下游收集器一步完成(性能更高,适合大数据量场景)
如果不需要保留分组后的Product列表,直接用自定义收集器一步统计:
public static Map<Double, TaxSummary> calculatingTaxes(List<Product> products){ return products.stream() .collect(Collectors.groupingBy( Product::getVat, Collector.of( TaxSummary::new, TaxSummary::addProduct, TaxSummary::merge ) )); }
运行结果
对你给出的测试数据,输出结果如下:
税率1.00 统计结果:TaxSummary{totalNetPrice=200.0, totalVatAmount=0.0, totalGrossPrice=200.0}
税率1.23 统计结果:TaxSummary{totalNetPrice=300.0, totalVatAmount=69.0, totalGrossPrice=369.0}
税率1.08 统计结果:TaxSummary{totalNetPrice=200.0, totalVatAmount=16.0, totalGrossPrice=216.0}
注意事项
- 生产环境用
double存储金额会有精度丢失风险,建议替换为BigDecimal类型 - 如果需要同时返回分组后的Product列表和统计结果,自定义实体将
List<Product>和TaxSummary组合即可
内容的提问来源于stack exchange,提问作者Jakub Kołacz
相关产品推荐
相关产品推荐

