0

0

使用Java Stream API高效实现对象列表按键分组到Map

花韻仙語

花韻仙語

发布时间:2025-07-22 13:18:23

|

1031人浏览过

|

来源于php中文网

原创

使用java stream api高效实现对象列表按键分组到map

本文将详细介绍如何使用Java Stream API将一个对象列表(如List)按照特定属性(如parent_id)高效地分组并映射到一个Map>结构中。文章会指出在处理一对多关系时,Collectors.toMap()的局限性,并重点阐述Collectors.groupingBy()作为正确且强大的解决方案,通过示例代码展示其简洁用法,帮助开发者避免常见错误,优化数据聚合逻辑。

在Java开发中,我们经常需要对集合数据进行转换和聚合。一个常见的需求是将一个对象列表根据某个属性进行分组,并将分组结果存储到Map中,其中Map的键是分组依据的属性值,而值是所有符合该属性的对象列表。例如,我们可能有一个Child对象的列表,每个Child对象都关联一个Parent,我们希望将所有Child对象按照其parent_id进行分组。

场景描述与常见误区

假设我们有以下数据结构:

// 父级实体
class Parent {
    private Long parentId;
    private String projectDesc;

    public Parent(Long parentId, String projectDesc) {
        this.parentId = parentId;
        this.projectDesc = projectDesc;
    }

    public Long getParentId() {
        return parentId;
    }
    // Getters and other methods
}

// 子级实体
class Child {
    private Long childId;
    private Parent parent; // 子级关联父级对象
    private String code;

    public Child(Long childId, Parent parent, String code) {
        this.childId = childId;
        this.parent = parent;
        this.code = code;
    }

    public Long getChildId() {
        return childId;
    }

    public Parent getParent() {
        return parent;
    }

    public String getCode() {
        return code;
    }

    @Override
    public String toString() {
        return "Child{" +
               "childId=" + childId +
               ", parentId=" + (parent != null ? parent.getParentId() : "null") +
               ", code='" + code + '\'' +
               '}';
    }
}

现在,我们有一个List,包含以下示例数据:

Parent parent1 = new Parent(1L, "One");
Parent parent2 = new Parent(2L, "Two");

List childEntityList = Arrays.asList(
    new Child(1L, parent1, "code1"),
    new Child(2L, parent1, "code2"),
    new Child(3L, parent1, "code3"),
    new Child(4L, parent2, "code4"),
    new Child(5L, parent2, "code5")
);

我们的目标是将其转换为Map>,其中键是parent_id,值是所有属于该parent_id的Child对象列表。

立即学习Java免费学习笔记(深入)”;

初学者在尝试使用Java Stream API实现此功能时,可能会倾向于使用Collectors.toMap(),如下所示:

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

// ... (Child and Parent class definitions as above)

// 错误示例:使用 toMap()
// Map> childEntityMap = childEntityList.stream()
//     .collect(Collectors.toMap(
//         childEntity ->  childEntity.getParent().getParentId(),
//         childEntity ->  childEntity
//     ));

上述代码在运行时会抛出IllegalStateException: Duplicate key ...异常。这是因为Collectors.toMap()默认期望每个键都是唯一的。当多个Child对象拥有相同的parent_id时(例如,child1、child2和child3都属于parent1),toMap()无法处理同一个键关联多个值的情况,从而导致异常。

CopyWeb
CopyWeb

AI网页设计转换工具,可以将屏幕截图、网站URL转换为代码组件

下载

正确的解决方案:使用 Collectors.groupingBy()

为了解决一对多(one-to-many)的映射问题,Java Stream API提供了专门的收集器:Collectors.groupingBy()。这个收集器正是为这种分组场景设计的。

Collectors.groupingBy()的基本用法是接收一个分类函数(classifier function),该函数用于从流中的每个元素中提取出作为Map键的值。默认情况下,所有被分类到同一个键的元素将被收集到一个List中作为Map的值。

以下是使用Collectors.groupingBy()正确实现上述需求的示例代码:

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Arrays; // For Arrays.asList

public class StreamGroupingExample {

    // ... (Child and Parent class definitions as above)

    public static void main(String[] args) {
        Parent parent1 = new Parent(1L, "One");
        Parent parent2 = new Parent(2L, "Two");

        List childEntityList = Arrays.asList(
            new Child(1L, parent1, "code1"),
            new Child(2L, parent1, "code2"),
            new Child(3L, parent1, "code3"),
            new Child(4L, parent2, "code4"),
            new Child(5L, parent2, "code5")
        );

        // 正确示例:使用 groupingBy()
        Map> childEntityMap = childEntityList.stream()
            .collect(Collectors.groupingBy(
                childEntity -> childEntity.getParent().getParentId()
            ));

        // 打印结果以验证
        childEntityMap.forEach((parentId, children) -> {
            System.out.println("Parent ID: " + parentId);
            children.forEach(child -> System.out.println("  " + child));
        });

        /*
        预期输出:
        Parent ID: 1
          Child{childId=1, parentId=1, code='code1'}
          Child{childId=2, parentId=1, code='code2'}
          Child{childId=3, parentId=1, code='code3'}
        Parent ID: 2
          Child{childId=4, parentId=2, code='code4'}
          Child{childId=5, parentId=2, code='code5'}
        */
    }
}

在上述代码中:

  1. childEntityList.stream() 创建了一个Child对象的流。
  2. collect(Collectors.groupingBy(...)) 是核心操作。
  3. childEntity -> childEntity.getParent().getParentId() 是分类函数,它从每个Child对象中提取出parent_id作为Map的键。
  4. groupingBy()默认会将所有具有相同parent_id的Child对象收集到一个List中,作为该parent_id对应的值。

注意事项与总结

  • 选择正确的收集器: 当你需要将流中的元素按照某个属性进行分组,并且每个键可能对应多个元素时,始终使用Collectors.groupingBy()。如果你确定每个键都是唯一的,并且希望将流转换为Map,那么Collectors.toMap()是合适的选择,但需注意处理键冲突的策略(例如使用mergeFunction)。
  • 默认行为: Collectors.groupingBy()的单参数版本默认将值收集到ArrayList中。如果你需要不同的集合类型(如HashSet),或者对收集到的值进行进一步的聚合,可以使用groupingBy的其他重载版本,它们允许你指定一个下游收集器(downstream collector)。
  • 可读性与简洁性: Java Stream API,尤其是Collectors类,提供了强大且富有表现力的方法来处理集合数据。理解并正确使用这些方法可以大大提高代码的可读性和简洁性。

通过本文的介绍,您应该已经掌握了如何使用Collectors.groupingBy()来高效地将对象列表按照指定属性分组到Map中,从而避免了toMap()在处理一对多关系时可能遇到的问题。掌握这一技巧对于编写高效、健壮的Java Stream代码至关重要。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

831

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

737

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

733

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16925

2023.08.03

Java 项目构建与依赖管理(Maven / Gradle)
Java 项目构建与依赖管理(Maven / Gradle)

本专题系统讲解 Java 项目构建与依赖管理的完整体系,重点覆盖 Maven 与 Gradle 的核心概念、项目生命周期、依赖冲突解决、多模块项目管理、构建加速与版本发布规范。通过真实项目结构示例,帮助学习者掌握 从零搭建、维护到发布 Java 工程的标准化流程,提升在实际团队开发中的工程能力与协作效率。

10

2026.01.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号