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

Java 8 Stream按3字段分组并对两个数值字段求和实现求助

Java 8 Stream多字段分组并求和实现方案

要实现按uuid、msgDate、channel三个字段分组,同时对incomingCount和outgoingCount求和,核心是定义正确的分组键,并通过Stream的收集器完成合并计算。以下是完整实现步骤:

1. 定义ReportData类

假设你的ReportData类结构如下(包含构造器、getter和toString方法):

import java.time.LocalDate;
import java.util.Objects;

public class ReportData {
    private String uuid;
    private LocalDate msgDate;
    private String channel;
    private int incomingCount;
    private int outgoingCount;

    public ReportData(String uuid, LocalDate msgDate, String channel, int incomingCount, int outgoingCount) {
        this.uuid = uuid;
        this.msgDate = msgDate;
        this.channel = channel;
        this.incomingCount = incomingCount;
        this.outgoingCount = outgoingCount;
    }

    // Getter方法
    public String getUuid() { return uuid; }
    public LocalDate getMsgDate() { return msgDate; }
    public String getChannel() { return channel; }
    public int getIncomingCount() { return incomingCount; }
    public int getOutgoingCount() { return outgoingCount; }

    @Override
    public String toString() {
        return "ReportData{" +
                "uuid='" + uuid + '\'' +
                ", msgDate=" + msgDate +
                ", channel='" + channel + '\'' +
                ", incomingCount=" + incomingCount +
                ", outgoingCount=" + outgoingCount +
                '}';
    }
}

2. 定义分组键类

由于需要按三个字段分组,需创建一个不可变的分组键类,并重写equals和hashCode方法(保证分组时键的唯一性):

private static class GroupKey {
    private final String uuid;
    private final LocalDate msgDate;
    private final String channel;

    public GroupKey(String uuid, LocalDate msgDate, String channel) {
        this.uuid = uuid;
        this.msgDate = msgDate;
        this.channel = channel;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        GroupKey groupKey = (GroupKey) o;
        return Objects.equals(uuid, groupKey.uuid) &&
                Objects.equals(msgDate, groupKey.msgDate) &&
                Objects.equals(channel, groupKey.channel);
    }

    @Override
    public int hashCode() {
        return Objects.hash(uuid, msgDate, channel);
    }
}

3. Stream分组求和实现

这里提供两种常用实现方式:

方式一:使用Collectors.groupingBy + Collectors.reducing

通过分组器将数据按GroupKey分类,再用归约器合并每个组内的求和字段:

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class GroupSumExample {
    public static void main(String[] args) {
        // 示例初始化数据
        List<ReportData> dataList = Arrays.asList(
                new ReportData("uuid1", LocalDate.of(2024, 5, 1), "channelA", 10, 5),
                new ReportData("uuid1", LocalDate.of(2024, 5, 1), "channelA", 20, 10),
                new ReportData("uuid1", LocalDate.of(2024, 5, 2), "channelB", 15, 8),
                new ReportData("uuid2", LocalDate.of(2024, 5, 1), "channelA", 5, 3)
        );

        // 分组求和
        Map<GroupKey, ReportData> groupedResult = dataList.stream()
                .collect(Collectors.groupingBy(
                        rd -> new GroupKey(rd.getUuid(), rd.getMsgDate(), rd.getChannel()),
                        Collectors.reducing(
                                new ReportData("", null, "", 0, 0), // 初始空对象
                                (rd1, rd2) -> new ReportData(
                                        rd1.getUuid(),
                                        rd1.getMsgDate(),
                                        rd1.getChannel(),
                                        rd1.getIncomingCount() + rd2.getIncomingCount(),
                                        rd1.getOutgoingCount() + rd2.getOutgoingCount()
                                )
                        )
                ));

        // 输出结果
        groupedResult.values().forEach(System.out::println);
    }

    // 分组键类定义(放在GroupSumExample类内部)
    private static class GroupKey {
        private final String uuid;
        private final LocalDate msgDate;
        private final String channel;

        public GroupKey(String uuid, LocalDate msgDate, String channel) {
            this.uuid = uuid;
            this.msgDate = msgDate;
            this.channel = channel;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            GroupKey groupKey = (GroupKey) o;
            return Objects.equals(uuid, groupKey.uuid) &&
                    Objects.equals(msgDate, groupKey.msgDate) &&
                    Objects.equals(channel, groupKey.channel);
        }

        @Override
        public int hashCode() {
            return Objects.hash(uuid, msgDate, channel);
        }
    }
}

方式二:使用Collectors.toMap

直接通过toMap收集器,指定键、值以及重复键时的合并规则,代码更简洁:

Map<GroupKey, ReportData> groupedResult = dataList.stream()
        .collect(Collectors.toMap(
                rd -> new GroupKey(rd.getUuid(), rd.getMsgDate(), rd.getChannel()),
                rd -> rd, // 直接用当前对象作为初始值
                (existing, newItem) -> new ReportData(
                        existing.getUuid(),
                        existing.getMsgDate(),
                        existing.getChannel(),
                        existing.getIncomingCount() + newItem.getIncomingCount(),
                        existing.getOutgoingCount() + newItem.getOutgoingCount()
                )
        ));

4. 期望输出结果

运行上述代码,输出如下(已按分组合并求和):

ReportData{uuid='uuid1', msgDate=2024-05-01, channel='channelA', incomingCount=30, outgoingCount=15}
ReportData{uuid='uuid1', msgDate=2024-05-02, channel='channelB', incomingCount=15, outgoingCount=8}
ReportData{uuid='uuid2', msgDate=2024-05-01, channel='channelA', incomingCount=5, outgoingCount=3}

注意事项

  • 如果msgDate使用java.util.Date,需注意其equals和hashCode基于时间戳,确保日期比较逻辑符合预期;
  • 分组键类必须正确重写equals和hashCode,否则会导致分组失败;
  • 若不想创建新的ReportData对象,可修改原对象(不推荐,会污染原始数据),建议始终创建新对象保存求和结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 06:27:08