能否将Java8 Stream中求和、统计非零数、求均值三步合并为一步?
当然可以!完全能把这三个步骤合并成一次Stream遍历,这样效率更高(不用多次遍历组件列表),还能把相关逻辑整合在一起,代码更紧凑。
方案1:Java 12+ 使用 Collectors.teeing
Java 12引入的teeing收集器正好适配这种场景——它会把每个元素同时传递给两个下游收集器,最后将两个收集器的结果合并成最终值。这里我们用一个收集器计算总成本,另一个过滤非零价格后统计数量,最后计算平均值:
private double calculateTotalBCMMatrixCost(SpecificationEntryRequest specificationEntryRequest) { return specificationEntryRequest.getComponentList().stream() .map(ComponentDetail::getStartingMaterialId) .map(this::calculateBCMMatrixCostForAnalyte) .collect(Collectors.teeing( // 第一个收集器:计算总成本 Collectors.summingDouble(Double::doubleValue), // 第二个收集器:统计非零价格的数量 Collectors.filtering(price -> price > 0, Collectors.counting()), // 合并结果:计算平均值(处理除数为0的边界情况) (totalCost, nonZeroCount) -> nonZeroCount == 0 ? 0.0 : totalCost / nonZeroCount )); }
方案2:Java 12以下版本 自定义收集容器
如果你的项目还在使用Java 12之前的版本,可以自定义一个简单的容器类,在一次遍历中同时累加总和和非零计数:
// 自定义容器类,用于存储总和和非零数量 private static class CostStats { double total = 0.0; long nonZeroCount = 0; // 处理单个价格 void accept(double price) { total += price; if (price > 0) { nonZeroCount++; } } // 合并多个容器(并行流场景下需要) CostStats combine(CostStats other) { this.total += other.total; this.nonZeroCount += other.nonZeroCount; return this; } } private double calculateTotalBCMMatrixCost(SpecificationEntryRequest specificationEntryRequest) { CostStats stats = specificationEntryRequest.getComponentList().stream() .map(ComponentDetail::getStartingMaterialId) .map(this::calculateBCMMatrixCostForAnalyte) .collect(CostStats::new, CostStats::accept, CostStats::combine); // 同样处理除数为0的情况,避免运行时异常 return stats.nonZeroCount == 0 ? 0.0 : stats.total / stats.nonZeroCount; }
额外提示
原代码存在一个潜在问题:如果所有价格都是0,nonZeroPriceCount会是0,直接做除法会抛出ArithmeticException。上面的两种方案都加入了边界处理,你可以根据业务需求调整(比如返回0、抛出自定义异常等)。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

