使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
问题背景
在开发中,我们经常需要根据某些规则对数据进行分组并构造成嵌套 Map。本例以学生信息为背景,展示如何用 Stream API
实现按班级分组并嵌套为以学生 ID 为键的 Map。
我们需要实现以下逻辑:
- 按班级分组:第一层 Map 的键是班级编号,值是另一个 Map。
- 按学生 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 的值。
优势分析
-
清晰简洁:
Stream API 的链式操作让代码直观且易于阅读。 -
灵活扩展:
可以轻松调整分组逻辑,例如按班级和性别进一步分组。 -
高效性:
分组操作直接在流中完成,无需手动遍历和构建 Map。
可能的改进
1. 处理重复键
若一个班级中存在重复的学生 ID,toMap
默认会抛出异常。可以通过提供合并函数解决:
Collectors.toMap(
Student::getStudentId,
Function.identity(),
(existing, replacement) -> existing // 保留已有值
)
2. 性能优化
对于大规模数据,使用并行流(parallelStream
)可以显著提高性能:
studentList.parallelStream()
.collect(Collectors.groupingBy(...));
总结
通过本文示例,我们展示了如何使用 Java 的 Stream API 高效地将学生信息列表组织成嵌套的 Map。groupingBy
和 toMap
是分组和嵌套操作的强大工具,能够帮助开发者更优雅地处理分层数据。
完整代码如下:
完整代码
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));
});
}
}
希望本文对你有帮助!如有问题,欢迎留言交流! 🎉