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

如何用Java Streams按Key分组对象并聚合唯一子对象集合

Java Streams 实现分组聚合方案

实体类参考(需确保B/C实现equals和hashCode)

// 源对象A0
class A0 {
    private Long id;
    private String name;
    private B b;
    private C c;

    // 构造器、Getter方法省略
}

// 目标对象A
class A {
    private Long id;
    private String name;
    private Set<B> uniqueBs;
    private Set<C> uniqueCs;

    // 全参构造器
    public A(Long id, String name, Set<B> uniqueBs, Set<C> uniqueCs) {
        this.id = id;
        this.name = name;
        this.uniqueBs = uniqueBs;
        this.uniqueCs = uniqueCs;
    }

    // Getter方法省略
}

// B、C类需正确实现equals和hashCode以保证集合去重
class B {
    // 字段、equals、hashCode实现
}

class C {
    // 字段、equals、hashCode实现
}

核心Streams转换实现

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

public class Converter {
    public List<A> convertA0ListToAList(List<A0> a0List) {
        return a0List.stream()
                // 按id+name分组,用自定义GroupKey作为分组键(比JDK内部Entry更规范)
                .collect(Collectors.groupingBy(
                        a0 -> new GroupKey(a0.getId(), a0.getName()),
                        // 对每个分组的A0列表聚合去重后的B、C集合,转换为A对象
                        Collectors.collectingAndThen(
                                Collectors.toList(),
                                group -> {
                                    // 提取并去重B对象,用LinkedHashSet保留顺序(可选换HashSet)
                                    Set<B> uniqueBs = group.stream()
                                            .map(A0::getB)
                                            .filter(Objects::nonNull) // 可选:过滤null值
                                            .collect(Collectors.toCollection(LinkedHashSet::new));
                                    // 提取并去重C对象
                                    Set<C> uniqueCs = group.stream()
                                            .map(A0::getC)
                                            .filter(Objects::nonNull)
                                            .collect(Collectors.toCollection(LinkedHashSet::new));
                                    // 从分组中取id和name(分组内所有A0的id/name一致)
                                    A0 first = group.get(0);
                                    return new A(first.getId(), first.getName(), uniqueBs, uniqueCs);
                                }
                        )
                ))
                .values() // 提取所有分组后的A对象
                .stream()
                .collect(Collectors.toList());
    }

    // 自定义不可变分组键类,确保分组逻辑正确
    private static class GroupKey {
        private final Long id;
        private final String name;

        public GroupKey(Long id, String name) {
            this.id = id;
            this.name = name;
        }

        @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(id, groupKey.id) && Objects.equals(name, groupKey.name);
        }

        @Override
        public int hashCode() {
            return Objects.hash(id, name);
        }
    }
}

关键要点说明

  • 分组键设计:用自定义GroupKey替代JDK的AbstractMap.SimpleEntry,避免依赖内部类,同时通过重写equals和hashCode保证分组逻辑准确。
  • 去重逻辑:必须确保B和C类正确实现equals和hashCode,否则Set无法完成去重。如果需要保留元素顺序,用LinkedHashSet;无需顺序则用HashSet即可。
  • 空值处理:添加filter(Objects::nonNull)可以避免将null值加入集合,根据业务需求可选择保留或移除该逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 23:10:31