根据子对象列表从列表创建地图

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