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

如何用Java 8 Stream计算CarData多字段平均值并生成单对象

用Java 8 Stream一次性生成多字段平均值的CarData

嘿,这个场景我太熟悉了!你不想手动拼接两个IntSummaryStatistics的结果,还想高效完成多字段的平均值统计,其实用自定义Collector就能完美解决,而且只需要遍历一次列表~

核心思路

既然要同时统计bodyWeight和shellWeight两个字段的总和与数量,最划算的方式是一次遍历完成所有统计,避免多次遍历列表(尤其是数据量大的时候)。我们可以自定义一个统计容器类,或者用数组临时存储统计数据,最后再转换成目标CarData对象。

方法一:自定义统计容器类(可读性最佳)

先写一个专门用来统计CarData字段的容器类,把总和、数量都存起来,最后再计算平均值生成CarData:

// 自定义统计容器,用来累积两个字段的总和与数据量
class CarStats {
    private int bodyWeightSum;
    private int shellWeightSum;
    private long count;

    // 空构造器,初始化默认值
    public CarStats() {
        this(0, 0, 0);
    }

    // 带参数的构造器
    public CarStats(int bodyWeightSum, int shellWeightSum, long count) {
        this.bodyWeightSum = bodyWeightSum;
        this.shellWeightSum = shellWeightSum;
        this.count = count;
    }

    // 累加单个CarData的数据
    public CarStats accept(CarData car) {
        return new CarStats(
            this.bodyWeightSum + car.getBodyWeight(),
            this.shellWeightSum + car.getShellWeight(),
            this.count + 1
        );
    }

    // 合并两个统计结果(并行流时会用到)
    public CarStats combine(CarStats other) {
        return new CarStats(
            this.bodyWeightSum + other.bodyWeightSum,
            this.shellWeightSum + other.shellWeightSum,
            this.count + other.count
        );
    }

    // 把统计结果转换成平均后的CarData
    public CarData toAverageCarData() {
        if (count == 0) {
            // 处理空列表的情况,根据需求返回null或默认CarData
            return null;
        }
        // 这里假设CarData有接收两个int参数的构造器,或者用Builder模式
        return new CarData(
            (int) (bodyWeightSum / count),
            (int) (shellWeightSum / count)
        );
    }
}

然后用Collector.of()把这个容器和Stream结合起来:

List<CarData> cars = getCars();
CarData averageCar = cars.stream()
    .collect(Collector.of(
        CarStats::new,          // 供应器:创建初始的空统计容器
        CarStats::accept,       // 累加器:把每个CarData加入统计
        CarStats::combine,      // 组合器:并行流时合并多个统计结果
        CarStats::toAverageCarData  // 终结器:把统计结果转成目标CarData
    ));

方法二:用数组临时存储统计数据(无需额外类)

如果不想写额外的容器类,也可以用一个int[]来临时存储[body总和, shell总和, 数据量],代码更简洁但可读性稍差:

List<CarData> cars = getCars();
CarData averageCar = cars.stream()
    .collect(Collector.of(
        () -> new int[]{0, 0, 0},  // 初始化数组:[bodySum, shellSum, count]
        (stats, car) -> {
            stats[0] += car.getBodyWeight();
            stats[1] += car.getShellWeight();
            stats[2]++;
        },
        (leftStats, rightStats) -> {  // 并行流时合并数组数据
            leftStats[0] += rightStats[0];
            leftStats[1] += rightStats[1];
            leftStats[2] += rightStats[2];
            return leftStats;
        },
        stats -> {
            if (stats[2] == 0) return null;  // 处理空列表
            return new CarData(stats[0]/stats[2], stats[1]/stats[2]);
        }
    ));

不推荐的方式:两次遍历列表

虽然用Collectors.averagingInt可以单独统计每个字段,但需要遍历列表两次,效率不高,适合小数据量场景:

// 不推荐:两次遍历列表,数据量大时性能较差
double avgBody = cars.stream().collect(Collectors.averagingInt(CarData::getBodyWeight));
double avgShell = cars.stream().collect(Collectors.averagingInt(CarData::getShellWeight));
CarData averageCar = new CarData((int) avgBody, (int) avgShell);

注意事项

  • 记得处理空列表的情况,避免出现除以0的异常;
  • 如果需要保留小数(比如平均值不是整数),可以把统计容器里的总和改成long类型,最后返回double类型的字段,或者调整CarData的字段类型。

内容的提问来源于stack exchange,提问作者Squiggs.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 07:52:51