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

使用Java Stream对对象数组按日分组并计算日均平均值

嘿,我来帮你搞定这个问题!你现在分开统计每日的总和和采样数确实有点重复工作,其实完全可以在一次Stream操作里完成这两个计算,直接得到每日的平均值,不用跑两次Stream~

解决方案:一次Stream计算每日平均值

方法1:使用Java 12+的Collectors.teeing(推荐)

Java 12引入的teeing收集器正好适配你的场景——它允许我们在一次分组操作中同时应用两个下游收集器,再把它们的结果合并起来。我们可以用它同时计算每日的平均值总和、采样数量,最后直接相除得到每日平均。

代码示例:

import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.stream.Collectors;

// 先写个可靠的日期分组辅助方法(替代原有的substring,避免toString格式不稳定)
private static String getDailyKey(Date date) {
    return new java.text.SimpleDateFormat("EEE MMM dd").format(date);
}

// 核心计算逻辑
Map<String, Double> dailyAverages = Arrays.stream(samples)
    .collect(Collectors.groupingBy(
        sample -> getDailyKey(sample.getTime()),
        Collectors.teeing(
            Collectors.summingDouble(PricingSample::getAverage), // 第一个收集器:统计每日总和
            Collectors.counting(), // 第二个收集器:统计每日采样数
            (totalSum, sampleCount) -> totalSum / sampleCount // 合并结果:计算平均值
        )
    ));

方法2:Java 8-11的兼容方案

如果你的项目还在用Java 8到11,没有teeing收集器,也有两种靠谱的方式实现:

方式A:自定义统计对象+分组

先定义一个简单的类来存储每日的总和与计数:

class DailyStats {
    private double sum;
    private long count;

    public void add(double average) {
        sum += average;
        count++;
    }

    public static DailyStats merge(DailyStats a, DailyStats b) {
        DailyStats merged = new DailyStats();
        merged.sum = a.sum + b.sum;
        merged.count = a.count + b.count;
        return merged;
    }

    public double getAverage() {
        return count == 0 ? 0 : sum / count;
    }
}

然后用groupingBy结合reducing收集器来整合数据:

Map<String, Double> dailyAverages = Arrays.stream(samples)
    .collect(Collectors.groupingBy(
        sample -> getDailyKey(sample.getTime()),
        Collectors.collectingAndThen(
            Collectors.reducing(
                new DailyStats(),
                sample -> {
                    DailyStats stats = new DailyStats();
                    stats.add(sample.getAverage());
                    return stats;
                },
                DailyStats::merge
            ),
            DailyStats::getAverage
        )
    ));

方式B:用数组临时存储总和与计数

如果不想额外定义类,可以用数组来轻量存储两个值:

// 先收集每日的总和[0]与计数[1]
Map<String, double[]> tempStats = Arrays.stream(samples)
    .collect(Collectors.groupingBy(
        sample -> getDailyKey(sample.getTime()),
        Collectors.reducing(
            new double[]{0, 0},
            sample -> new double[]{sample.getAverage(), 1},
            (a, b) -> new double[]{a[0] + b[0], a[1] + b[1]}
        )
    ));

// 转换为最终的平均值Map
Map<String, Double> dailyAverages = tempStats.entrySet().stream()
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        entry -> entry.getValue()[0] / entry.getValue()[1]
    ));

小优化:让日期分组更可靠

你原来用e.getTime().toString().substring(0,10)分组,虽然能工作,但Date.toString()的输出依赖系统Locale,可能在不同环境下格式不一致。如果是Java 8+,建议把Date转成LocalDate作为分组key,更类型安全:

import java.time.LocalDate;
import java.time.ZoneId;

private static LocalDate getLocalDate(Date date) {
    return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}

// 直接用LocalDate分组
Map<LocalDate, Double> dailyAverages = Arrays.stream(samples)
    .collect(Collectors.groupingBy(
        sample -> getLocalDate(sample.getTime()),
        Collectors.teeing(
            Collectors.summingDouble(PricingSample::getAverage),
            Collectors.counting(),
            (sum, count) -> sum / count
        )
    ));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:39:43