使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>

使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>


问题背景

在开发中,我们经常需要根据某些规则对数据进行分组并构造成嵌套 Map。本例以学生信息为背景,展示如何用 Stream API 实现按班级分组并嵌套为以学生 ID 为键的 Map。

我们需要实现以下逻辑:

  1. 按班级分组:第一层 Map 的键是班级编号,值是另一个 Map。
  2. 按学生 ID 分组:第二层 Map 的键是学生 ID,值是学生信息对象。

最终数据结构:

java 复制代码
Map<String, Map<String, Student>> classStudentMap;

数据结构

Student
java 复制代码
public class Student {
    private String classId;  // 班级 ID
    private String studentId; // 学生 ID
    private String name;      // 学生姓名

    // 构造器
    public Student(String classId, String studentId, String name) {
        this.classId = classId;
        this.studentId = studentId;
        this.name = name;
    }

    // Getter 方法
    public String getClassId() {
        return classId;
    }

    public String getStudentId() {
        return studentId;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "classId='" + classId + '\'' +
                ", studentId='" + studentId + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

核心代码

下面是使用 Stream API 将学生信息列表分组并嵌套为 Map 的实现:

java 复制代码
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class StudentGroupingExample {
    public static void main(String[] args) {
        // 示例数据
        List<Student> studentList = Arrays.asList(
            new Student("Class1", "S1", "Alice"),
            new Student("Class1", "S2", "Bob"),
            new Student("Class2", "S3", "Charlie"),
            new Student("Class2", "S4", "Daisy"),
            new Student("Class3", "S5", "Eve")
        );

        // 使用 Stream API 分组
        Map<String, Map<String, Student>> classStudentMap = studentList.stream()
            .collect(Collectors.groupingBy(
                Student::getClassId, // 第一层分组:按班级 ID
                Collectors.toMap(
                    Student::getStudentId, // 第二层分组:按学生 ID
                    Function.identity()    // 以学生对象本身为值
                )
            ));

        // 打印嵌套 Map
        classStudentMap.forEach((classId, students) -> {
            System.out.println("Class: " + classId);
            students.forEach((studentId, student) ->
                System.out.println("  Student ID: " + studentId + " -> " + student));
        });
    }
}

示例运行结果

输入数据:
java 复制代码
List<Student> studentList = Arrays.asList(
    new Student("Class1", "S1", "Alice"),
    new Student("Class1", "S2", "Bob"),
    new Student("Class2", "S3", "Charlie"),
    new Student("Class2", "S4", "Daisy"),
    new Student("Class3", "S5", "Eve")
);
输出结果:
复制代码
Class: Class1
  Student ID: S1 -> Student{classId='Class1', studentId='S1', name='Alice'}
  Student ID: S2 -> Student{classId='Class1', studentId='S2', name='Bob'}
Class: Class2
  Student ID: S3 -> Student{classId='Class2', studentId='S3', name='Charlie'}
  Student ID: S4 -> Student{classId='Class2', studentId='S4', name='Daisy'}
Class: Class3
  Student ID: S5 -> Student{classId='Class3', studentId='S5', name='Eve'}

核心解析

1. 分组实现逻辑
  • 第一层分组 :使用 Collectors.groupingBy 按班级 ID 对学生列表分组。
  • 第二层 Map :在 groupingBy 的下游 Collector 中,使用 Collectors.toMap 将分组结果进一步组织为按学生 ID 的 Map。
2. Function.identity() 的作用

Function.identity()Function<T, T> 的快捷方法,表示值本身。例如,学生对象直接作为第二层 Map 的值。


优势分析

  1. 清晰简洁

    Stream API 的链式操作让代码直观且易于阅读。

  2. 灵活扩展

    可以轻松调整分组逻辑,例如按班级和性别进一步分组。

  3. 高效性

    分组操作直接在流中完成,无需手动遍历和构建 Map。


可能的改进

1. 处理重复键

若一个班级中存在重复的学生 ID,toMap 默认会抛出异常。可以通过提供合并函数解决:

java 复制代码
Collectors.toMap(
    Student::getStudentId,
    Function.identity(),
    (existing, replacement) -> existing // 保留已有值
)
2. 性能优化

对于大规模数据,使用并行流(parallelStream)可以显著提高性能:

java 复制代码
studentList.parallelStream()
    .collect(Collectors.groupingBy(...));

总结

通过本文示例,我们展示了如何使用 Java 的 Stream API 高效地将学生信息列表组织成嵌套的 Map。groupingBytoMap 是分组和嵌套操作的强大工具,能够帮助开发者更优雅地处理分层数据。

完整代码如下:


完整代码

java 复制代码
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class StudentGroupingExample {
    public static void main(String[] args) {
        List<Student> studentList = Arrays.asList(
            new Student("Class1", "S1", "Alice"),
            new Student("Class1", "S2", "Bob"),
            new Student("Class2", "S3", "Charlie"),
            new Student("Class2", "S4", "Daisy"),
            new Student("Class3", "S5", "Eve")
        );

        Map<String, Map<String, Student>> classStudentMap = studentList.stream()
            .collect(Collectors.groupingBy(
                Student::getClassId,
                Collectors.toMap(
                    Student::getStudentId,
                    Function.identity()
                )
            ));

        classStudentMap.forEach((classId, students) -> {
            System.out.println("Class: " + classId);
            students.forEach((studentId, student) ->
                System.out.println("  Student ID: " + studentId + " -> " + student));
        });
    }
}

希望本文对你有帮助!如有问题,欢迎留言交流! 🎉

相关推荐
学习编程的Kitty15 小时前
算法——位运算
java·前端·算法
斑点鱼 SpotFish15 小时前
用Python可视化国庆期间旅游概况与消费趋势
开发语言·python·旅游
小兔崽子去哪了15 小时前
Python 学习记录
python
用户9047066835715 小时前
如何使用 Spring MVC 实现 RESTful API 接口
java·后端
刘某某.15 小时前
数组和小于等于k的最长子数组长度b
java·数据结构·算法
程序员飞哥15 小时前
真正使用的超时关单策略是什么?
java·后端·面试
用户9047066835715 小时前
SpringBoot 多环境配置与启动 banner 修改
java·后端
埃泽漫笔15 小时前
RabbitMQ四种交换机详解
python·mq
小old弟15 小时前
后端三层架构
java·后端
花花鱼15 小时前
spring boot 2.x 与 spring boot 3.x 及对应Tomcat、Jetty、Undertow版本的选择(理论)
java·后端