使用 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;  // 班级 IDprivate String studentId; // 学生 IDprivate 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;}@Overridepublic 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, // 第一层分组:按班级 IDCollectors.toMap(Student::getStudentId, // 第二层分组:按学生 IDFunction.identity()    // 以学生对象本身为值)));// 打印嵌套 MapclassStudentMap.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: Class1Student ID: S1 -> Student{classId='Class1', studentId='S1', name='Alice'}Student ID: S2 -> Student{classId='Class1', studentId='S2', name='Bob'}
Class: Class2Student ID: S3 -> Student{classId='Class2', studentId='S3', name='Charlie'}Student ID: S4 -> Student{classId='Class2', studentId='S4', name='Daisy'}
Class: Class3Student 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.rhkb.cn/news/477116.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实时质检-静音检测分析流程(运维人员使用)

前言 用户在实时质检时&#xff0c;开启了主叫或被叫静音检测功能&#xff0c;但是听录音时&#xff0c;主叫或被叫明明没有任何声音&#xff0c;但是通话没有被挂断。 说明主叫或被叫的静音阈值太低&#xff0c;导致系统没有把很小的声音认定为静音&#xff1b;或者检测非静音…

MetaGPT实现多动作Agent

异步编程学习链接 智能体 LLM观察思考行动记忆 多智能体 智能体环境SOP评审路由订阅经济 教程地址 多动作的agent的本质是react&#xff0c;这包括了think&#xff08;考虑接下来该采取啥动作&#xff09;act&#xff08;采取行动&#xff09; 在MetaGPT的examples/write_…

【MySQL】MySQL数据库基础

【MySQL】MySQL数据库基础 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;MySQL&#x1f34b; &#x1f33c;文章目录&#x1f33c; 1. 数据库基础 1.1 什么是数据库 1.2 主流数据库 1.3 MySQL基本使用 1.3.1 服务器&#xff0c;数据…

进程控制(详解)

一.进程创建 1.fork函数 在linux中fork函数是⾮常重要的函数&#xff0c;它从已存在进程中创建⼀个新进程。新进程为⼦进程&#xff0c;⽽原进 程为⽗进程。 #include <unistd.h>pid_t fork(void);返回值&#xff1a;⾃进程中返回0&#xff0c;⽗进程返回⼦进程id&…

RFSOC 49dr 开发板,支持12收5发

硬件支持: 1,12收5发 2.4X25G光模块 3.J30J扩展接口 4.支持多板同步&#xff0c;多TILE同步

生产制造领域的多元化模式探索

在当今全球化和信息化的时代背景下&#xff0c;生产制造领域正经历着前所未有的变革。随着消费者需求的多样化、市场竞争的加剧以及技术的不断进步&#xff0c;传统的生产制造模式已经难以满足现代企业的需求。因此&#xff0c;多种生产制造模式应运而生&#xff0c;以适应不同…

基于YOLOv8深度学习的智慧农业果园果树苹果类果实目标检测系统(PyQt5界面+数据集+训练代码)

随着智慧农业技术的快速发展&#xff0c;果园管理逐渐向自动化和智能化方向迈进&#xff0c;传统的果园管理方式面临着高成本、效率低以及人工依赖程度大的挑战。在这种背景下&#xff0c;基于人工智能的目标检测技术为果园管理提供了一种全新的解决方案。本研究设计并实现了一…

【泥石流;风险;脆弱性;风险评估;川藏公路北线|论文解读4】川藏高速公路北线泥石流风险评估

【泥石流&#xff1b;风险&#xff1b;脆弱性&#xff1b;风险评估&#xff1b;川藏公路北线|论文解读4】川藏高速公路北线泥石流风险评估 【泥石流&#xff1b;风险&#xff1b;脆弱性&#xff1b;风险评估&#xff1b;川藏公路北线|论文解读4】川藏高速公路北线泥石流风险评…

mysql的优化

1、概念 在应用开发的初期&#xff0c;由于数据量较小&#xff0c;开发人员更重视功能上的实现&#xff0c;随着应用系统上线后&#xff0c;数据量急剧增长&#xff0c;很多性能问题逐渐显现&#xff0c;对使用的影响也越来越大&#xff0c;此时这些问题语句就称为整个系统的性…

栈的应用,力扣394.字符串解码力扣946.验证栈序列力扣429.N叉树的层序遍历力扣103.二叉树的锯齿形层序遍历

目录 力扣394.字符串解码 力扣946.验证栈序列 力扣429.N叉树的层序遍历 力扣103.二叉树的锯齿形层序遍历 力扣394.字符串解码 看见括号&#xff0c;由内而外&#xff0c;转向用栈解决。使用两个栈处理&#xff0c;一个用String,一个用Integer 遇到数字:提取数字放入到数字栈…

【Python系列】 Base64 编码:使用`base64`模块

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Mac 修改默认jdk版本

当前会话生效 这里演示将 Java 17 版本降低到 Java 8 查看已安装的 Java 版本&#xff1a; 在终端&#xff08;Terminal&#xff09;中运行以下命令&#xff0c;查看已安装的 Java 版本列表 /usr/libexec/java_home -V设置默认 Java 版本&#xff1a; 找到 Java 8 的安装路…

C++ STL - vector/list讲解及迭代器失效

vector 使用 vector 是一个动态数组. 构造/拷贝构造/赋值重载函数 int main() {// 是一个模板, 在实例化的时候, 需要指明类型std::vector<int> first; // 一个空的数组std::vector<int> second (4,100); // 设置初始空间大小为 4 个int, 全部初始化为 100std::v…

libphone desktop编译

linphone-desktop 在ubuntu20.04 下编译 linphone 介绍 Linphone是一款遵循GPL的开源网络视频电话系统&#xff0c;支持多种平台如Windows、Linux、Android等。它基于SIP协议&#xff0c;提供语音、视频通话及即时文本消息功能。核心功能包括SIP用户代理、音频视频Codec支持、…

根据已知站点寻找路网的最短路径

背景 接上期&#xff0c;基于MATSim的交通仿真&#xff0c;其中有一块非常重要的就是公交的仿真&#xff0c;这也是当初选择MATSim技术路线的一个重要原因&#xff0c;现在业务给出的场景是上传一些有序站点及其经纬度&#xff0c;需要通过算法来适配对应的路网&#xff0c;由…

Jenkins + gitee 自动触发项目拉取部署(Webhook配置)

目录 前言 Generic Webhook Trigger 插件 下载插件 ​编辑 配置WebHook 生成tocken 总结 前言 前文简单介绍了Jenkins环境搭建&#xff0c;本文主要来介绍一下如何使用 WebHook 触发自动拉取构建项目&#xff1b; Generic Webhook Trigger 插件 实现代码推送后&#xff0c;触…

leetcode 919.完全二叉树插入器

1.题目要求: 完全二叉树 是每一层&#xff08;除最后一层外&#xff09;都是完全填充&#xff08;即&#xff0c;节点数达到最大&#xff09;的&#xff0c;并且所有的节点都尽可能地集中在左侧。设计一种算法&#xff0c;将一个新节点插入到一棵完全二叉树中&#xff0c;并在…

SSL协议

文章目录 1. 前言2. 基础概念3. SSL协议结构3.1 概述3.2 SSL握手协议3.3 修改密码说明协议3.4 报警协议3.5 SSL记录协议 4. SSL安全性4.1 安全机制分析4.2 脆弱性分析 5. SSL证书 1. 前言 参考《应用系统安全基础》 2. 基础概念 安全套接字层协议&#xff08;Security Socke…

Flink-Source的使用

Data Sources 是什么呢&#xff1f;就字面意思其实就可以知道&#xff1a;数据来源。 Flink 做为一款流式计算框架&#xff0c;它可用来做批处理&#xff0c;也可以用来做流处理&#xff0c;这个 Data Sources 就是数据的来源地。 flink在批/流处理中常见的source主要有两大类…

Linux线程_线程控制_线程库

一.线程控制 在Linux操作系统的视角&#xff0c;Linux下没有真正意义上的线程&#xff0c;而是用进程模拟的线程&#xff08;LWP&#xff09;。所以&#xff0c;Linux不会提供直接创建线程的系统调用&#xff0c;而是提供创建轻量级进程的接口。但是由于用户只认线程&#xff0…