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

Java 8按单字段分组并映射至多字段的技术实现问询

解决方案

刚好做过类似的需求,用Java 8 Stream的分组+自定义收集器就能完美解决。核心思路是先按userId把所有记录归到一组,再把每组内的多条记录合并成一个Person对象,同时聚合courses集合,顺便保留用户的姓名信息。

高效实现代码

先给你最优化的版本,避免重复遍历数据:

import java.util.*;
import java.util.stream.Collectors;

public class PersonRecordMerger {

    // 核心合并方法
    public static List<Person> mergeDbRecords(List<Map<String, Object>> dbQueryResults) {
        return dbQueryResults.stream()
                // 按userId分组,同时用自定义收集器处理每组数据
                .collect(Collectors.groupingBy(
                        record -> (String) record.get("userId"),
                        Collector.of(
                                // 1. 供应商:创建临时容器,存Person和待聚合的courses
                                () -> new AbstractMap.SimpleEntry<>(null, new HashSet<Courses>()),
                                // 2. 累加器:处理组内每条记录
                                (tempContainer, record) -> {
                                    // 第一次处理该组时,初始化Person对象
                                    if (tempContainer.getKey() == null) {
                                        Person person = new Person();
                                        person.setUserId((String) record.get("userId"));
                                        person.setFirstName((String) record.get("firstName"));
                                        person.setLastName((String) record.get("lastName"));
                                        tempContainer.setKey(person);
                                    }
                                    // 将当前记录的course加入集合
                                    tempContainer.getValue().add((Courses) record.get("course"));
                                },
                                // 3. 组合器:并行流场景下合并两个容器(可选,但规范要写)
                                (container1, container2) -> {
                                    if (container1.getKey() == null) return container2;
                                    container1.getValue().addAll(container2.getValue());
                                    return container1;
                                },
                                // 4. 终止器:把聚合好的courses赋值给Person,返回最终对象
                                tempContainer -> {
                                    tempContainer.getKey().setCourses(tempContainer.getValue());
                                    return tempContainer.getKey();
                                }
                        )
                ))
                .values() // 取出所有分组后的Person对象
                .stream()
                .collect(Collectors.toList());
    }

    // 你的Person类(补充必要的getter/setter)
    static class Person {
        String userId;
        String firstName;
        String lastName;
        Set<Courses> courses = new HashSet<>();

        public String getUserId() { return userId; }
        public void setUserId(String userId) { this.userId = userId; }
        public String getFirstName() { return firstName; }
        public void setFirstName(String firstName) { this.firstName = firstName; }
        public String getLastName() { return lastName; }
        public void setLastName(String lastName) { this.lastName = lastName; }
        public Set<Courses> getCourses() { return courses; }
        public void setCourses(Set<Courses> courses) { this.courses = courses; }
    }

    // 你的Courses类(必须重写equals和hashCode,否则HashSet无法去重)
    static class Courses {
        private String courseId;

        public Courses(String courseId) { this.courseId = courseId; }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Courses courses = (Courses) o;
            return Objects.equals(courseId, courses.courseId);
        }

        @Override
        public int hashCode() { return Objects.hash(courseId); }
    }
}

关键细节说明

  1. 分组逻辑:用Collectors.groupingBy按userId分组,确保同一个用户的所有记录被归到一组。
  2. 自定义收集器:Collector.of是核心,四个参数分别负责:
    • 初始化临时容器,用来暂存Person对象和待聚合的课程集合
    • 遍历组内每条记录,初始化Person并把课程加入集合
    • 并行流场景下合并两个容器的内容(如果不用并行流,这个逻辑基本不会触发,但规范上要实现)
    • 最后把聚合好的课程集合赋值给Person,返回最终对象
  3. Courses类的坑:一定要重写equals和hashCode,不然HashSet没法识别重复的课程,会导致集合里出现重复数据。
  4. 姓名一致性:默认假设同一个userId下的firstName和lastName是一致的(符合你给的示例数据),如果实际场景中有不一致的情况,可以在累加器里加判断逻辑(比如取第一条记录的值,或者抛出异常提示数据问题)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:56:30