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

如何用Java Stream优化Measurement列表的加权平均计算?

优化Java 17下加权平均计算的实现方案

问题背景

给定如下Measurement类:

@Getter
@AllArgsConstructor
public class Measurement {
    private BigDecimal rawValue;
    private BigDecimal weighting;
}

需要处理List<Measurement>计算加权平均,步骤为:

  • 将每个rawValue与对应的weighting相乘
  • 求和所有加权后的原始值
  • 将加权值总和除以所有权重的总和

当前实现使用两次流分别计算两个总和,不够简洁,需优化为Java 17环境下更高效简洁的实现。

优化方案一:使用Collectors.teeing一次遍历完成计算

Java 12引入的Collectors.teeing可在单次流处理中同时收集两个结果,完美适配此场景——一次遍历即可同时计算加权值总和与权重总和,避免重复遍历集合。

完整实现代码

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

public class WeightedAverageCalculator {

    public BigDecimal calculateWeightedAverage(List<Measurement> measurements) {
        if (measurements == null || measurements.isEmpty()) {
            return BigDecimal.ZERO; // 可根据业务需求改为抛出异常或返回null
        }

        return measurements.stream()
                .collect(Collectors.teeing(
                        // 收集器1:计算rawValue*weighting的总和
                        Collectors.reducing(BigDecimal.ZERO,
                                m -> m.getRawValue().multiply(m.getWeighting()),
                                BigDecimal::add),
                        // 收集器2:计算所有权重的总和
                        Collectors.reducing(BigDecimal.ZERO,
                                Measurement::getWeighting,
                                BigDecimal::add),
                        // 合并两个结果:加权值总和 / 权重总和
                        (weightedSum, weightSum) -> {
                            if (weightSum.compareTo(BigDecimal.ZERO) == 0) {
                                return BigDecimal.ZERO; // 避免除以零,按需调整逻辑
                            }
                            // 可根据业务需求更换舍入模式,例如ROUND_HALF_EVEN
                            return weightedSum.divide(weightSum, BigDecimal.ROUND_HALF_UP);
                        }));
    }
}

关键说明

  • Collectors.teeing:每个流元素会被同时传入两个收集器处理,最终合并两个收集结果。
  • 边界处理:提前判空避免无效计算,加入权重总和为零的判断防止算术异常。
  • 舍入模式:BigDecimal.divide必须指定舍入规则,示例中用ROUND_HALF_UP(四舍五入),可根据业务调整。

优化方案二:自定义容器类遍历计算

若偏好传统遍历方式,可自定义一个简单容器存储两个总和,单次遍历完成计算:

import java.math.BigDecimal;
import java.util.List;

public class WeightedAverageCalculator {

    private static class SumContainer {
        private BigDecimal weightedSum = BigDecimal.ZERO;
        private BigDecimal weightSum = BigDecimal.ZERO;

        private void accumulate(Measurement measurement) {
            weightedSum = weightedSum.add(measurement.getRawValue().multiply(measurement.getWeighting()));
            weightSum = weightSum.add(measurement.getWeighting());
        }
    }

    public BigDecimal calculateWeightedAverage(List<Measurement> measurements) {
        if (measurements == null || measurements.isEmpty()) {
            return BigDecimal.ZERO;
        }

        SumContainer container = new SumContainer();
        measurements.forEach(container::accumulate);

        if (container.weightSum.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return container.weightedSum.divide(container.weightSum, BigDecimal.ROUND_HALF_UP);
    }
}

这个方案逻辑直观,同样只遍历一次集合,适合不需要使用流API的场景。

内容的提问来源于stack exchange,提问作者Robert Strauch

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 03:16:14