根据子对象列表从列表创建地图
Create map from list based on list of child objects
我有一个 class,其中包含子对象列表,如下所示:
class Student {
private String name;
private List<String> subjects;
}
我有一份学生 class 的列表,如下所示:
Student [name = "John", subjects = ["Physics", "Math", "Chemistry"]]
Student [name = "Max", subjects = ["Physics", "Chemistry"]]
Student [name = "Chris", subjects = ["Chemistry"]]
我想对数据进行分组,如下所示:
Physics = [Student [name = "John", subjects = ["Physics", "Math", "Chemistry"]],
Student [name = "Max", subjects = ["Physics", "Chemistry"]]],
Chemistry = [Student [name = "John", subjects = ["Physics", "Math", "Chemistry"]],
Student [name = "Max", subjects = ["Physics", "Chemistry"]],
Student [name = "Chris", subjects = ["Chemistry"]]]
Math = [Student [name = "John", subjects = ["Physics", "Math", "Chemistry"]]]
我可以使用传统的 foreach 循环对数据进行分组,但我想为此使用流 API,但无法这样做。
你能帮忙吗?
一种方法是将学生映射成一对 (subject, student)
,然后按学科对他们进行分组。
List<Student> students = Arrays.asList(
new Student("John", Arrays.asList("Physics", "Math", "Chemistry")),
new Student("Max", Arrays.asList("Physics", "Chemistry")),
new Student("Chris", Arrays.asList("Chemistry"))
);
Map<String, List<Student>> result = students.stream()
.flatMap(student -> student
.getSubjects()
.stream()
.map(s -> new Object[]{s, student})
)
.collect(
Collectors.groupingBy(pair -> (String) pair[0],
Collectors.mapping(pair -> (Student) pair[1], Collectors.toList()))
);
System.out.println(result);
这类问题的第一步是了解如何对其元素进行分组。
棘手的部分是我们不打算按简单的 属性 对学生进行分组,而是使用聚合 属性,一组学科。
一个解决方案是将每个学生的主题用作键,将学生本身用作映射的值:
BiConsumer<HashMap<String, List<Student>>, Student> mapStudentBySubject =
((hashMap, student) -> student.getSubjects().forEach(subject ->
hashMap.computeIfAbsent(subject, k -> new ArrayList<>()).add(student))
);
使用 computIfAbsent 方法的原因只是为了保证每个 Subject 都有一个列表
其余代码非常简单:
var studentsGrouped = studentBundle.stream()
.collect(HashMap::new, mapStudentBySubject, HashMap::putAll);
完整代码:
var john = new Student("John", List.of("Physics", "Math", "Chemistry"));
var max = new Student("Max", List.of("Physics", "Chemistry"));
var chris = new Student("Chris", List.of("Chemistry"));
var studentBundle = List.of(john, max, chris);
BiConsumer<HashMap<String, List<Student>>, Student> mapStudentBySubject =
((hashMap, student) -> student.getSubjects().forEach(subject ->
hashMap.computeIfAbsent(subject, k -> new ArrayList<>()).add(student))
);
var studentsGrouped = studentBundle.stream()
.collect(HashMap::new, mapStudentBySubject, HashMap::putAll);
我有一个 class,其中包含子对象列表,如下所示:
class Student {
private String name;
private List<String> subjects;
}
我有一份学生 class 的列表,如下所示:
Student [name = "John", subjects = ["Physics", "Math", "Chemistry"]]
Student [name = "Max", subjects = ["Physics", "Chemistry"]]
Student [name = "Chris", subjects = ["Chemistry"]]
我想对数据进行分组,如下所示:
Physics = [Student [name = "John", subjects = ["Physics", "Math", "Chemistry"]],
Student [name = "Max", subjects = ["Physics", "Chemistry"]]],
Chemistry = [Student [name = "John", subjects = ["Physics", "Math", "Chemistry"]],
Student [name = "Max", subjects = ["Physics", "Chemistry"]],
Student [name = "Chris", subjects = ["Chemistry"]]]
Math = [Student [name = "John", subjects = ["Physics", "Math", "Chemistry"]]]
我可以使用传统的 foreach 循环对数据进行分组,但我想为此使用流 API,但无法这样做。 你能帮忙吗?
一种方法是将学生映射成一对 (subject, student)
,然后按学科对他们进行分组。
List<Student> students = Arrays.asList(
new Student("John", Arrays.asList("Physics", "Math", "Chemistry")),
new Student("Max", Arrays.asList("Physics", "Chemistry")),
new Student("Chris", Arrays.asList("Chemistry"))
);
Map<String, List<Student>> result = students.stream()
.flatMap(student -> student
.getSubjects()
.stream()
.map(s -> new Object[]{s, student})
)
.collect(
Collectors.groupingBy(pair -> (String) pair[0],
Collectors.mapping(pair -> (Student) pair[1], Collectors.toList()))
);
System.out.println(result);
这类问题的第一步是了解如何对其元素进行分组。
棘手的部分是我们不打算按简单的 属性 对学生进行分组,而是使用聚合 属性,一组学科。
一个解决方案是将每个学生的主题用作键,将学生本身用作映射的值:
BiConsumer<HashMap<String, List<Student>>, Student> mapStudentBySubject =
((hashMap, student) -> student.getSubjects().forEach(subject ->
hashMap.computeIfAbsent(subject, k -> new ArrayList<>()).add(student))
);
使用 computIfAbsent 方法的原因只是为了保证每个 Subject 都有一个列表
其余代码非常简单:
var studentsGrouped = studentBundle.stream()
.collect(HashMap::new, mapStudentBySubject, HashMap::putAll);
完整代码:
var john = new Student("John", List.of("Physics", "Math", "Chemistry"));
var max = new Student("Max", List.of("Physics", "Chemistry"));
var chris = new Student("Chris", List.of("Chemistry"));
var studentBundle = List.of(john, max, chris);
BiConsumer<HashMap<String, List<Student>>, Student> mapStudentBySubject =
((hashMap, student) -> student.getSubjects().forEach(subject ->
hashMap.computeIfAbsent(subject, k -> new ArrayList<>()).add(student))
);
var studentsGrouped = studentBundle.stream()
.collect(HashMap::new, mapStudentBySubject, HashMap::putAll);