当前位置: 首页 > article >正文

使用 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,值是学生信息对象。

最终数据结构:

Map<String, Map<String, Student>> classStudentMap;

数据结构

Student
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 的实现:

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));
        });
    }
}

示例运行结果

输入数据:
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 默认会抛出异常。可以通过提供合并函数解决:

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

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

studentList.parallelStream()
    .collect(Collectors.groupingBy(...));

总结

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

完整代码如下:


完整代码

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));
        });
    }
}

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


http://www.kler.cn/a/406483.html

相关文章:

  • Android上运行Opencv(TODO)
  • 深入探究蓝牙节能技术:SNIFF与HOLD模式
  • 基于RTEMS项目学习waf build system
  • Cmakelist.txt之win-c-udp-client
  • GRU (门控循环单元 - 基于RNN - 简化LSTM又快又好 - 体现注意力的思想) + 代码实现 —— 笔记3.5《动手学深度学习》
  • 主要用于图像的颜色提取、替换以及区域修改
  • 6、PyTorch中搭建分类网络实例
  • 对抗样本存在的原因
  • 鸿蒙NEXT开发-Navigation组件导航
  • 用 Python 写了一个俄罗斯方块小游戏(附源码)
  • 机器人打包物品研究现状简述
  • stm32启动过程解析startup启动文件
  • 【分享一个vue指令】鼠标放置提示指令v-tooltip
  • 【git】husky - pre-commit script failed (code 2)
  • 数据库课程设计全流程:方法与实例解析
  • C#调用JAVA
  • Rust编程与项目实战-模块std::thread(之一)
  • Rust 的静态网站生成器「GitHub 热点速览」
  • VTK知识学习(11)- 可视化管线
  • 第J7周:对于ResNeXt-50算法的思考
  • Linux Docker 部署 Jenkins 详解教程
  • C#里怎么样判断文件是否存在?
  • Idea修改Commit Changes模式、idea使用git缺少部分Commit Changes
  • 基本的SELECT语句
  • 在 Ubuntu 系统上安装 npm 环境以及 nvm(Node Version Manager)
  • 车企如何实现安全图纸外发管理