Java流 - 按嵌套列表(第二级列表)进行分组

8
我有以下数据结构 -
每个学生都有一个州列表,每个州都有一个城市列表。
public class Student {
    private int id;
    private String name;
    private List<State> states = new ArrayList<>();
}

public class State {
    private int id;
    private String name;
    private List<City> Cities = new ArrayList<>();
}

public class City {
    private int id;
    private String name;
}

我希望获取以下内容。

Map<String, Students> citiesIdsToStudensList;

我写下了以下内容

Map<Integer, List<Integer>> statesToStudentsMap = students.stream()
            .flatMap(student -> student.getStates().stream())
            .flatMap(state -> state.getCities().stream())
            .collect(Collectors.groupingBy(City::getId, Collectors.mapping(x -> x.getId(), Collectors.toList())));

但这并没有给我想要的结果。
2个回答

6
使用Stream API,您需要进行两次flat map操作,并将每个中间的学生和城市映射到一个元组中,该元组能够容纳该学生。
Map<Integer, List<Student>> citiesIdsToStudentsList =
    students.stream()
            .flatMap(student -> student.getStates().stream().map(state -> new AbstractMap.SimpleEntry<>(student, state)))
            .flatMap(entry -> entry.getValue().getCities().stream().map(city -> new AbstractMap.SimpleEntry<>(entry.getKey(), city)))
            .collect(Collectors.groupingBy(
                entry -> entry.getValue().getId(),
                Collectors.mapping(Map.Entry::getKey, Collectors.toList())
            ));

然而,在这里使用嵌套的 for 循环可能更加清晰:

Map<Integer, List<Student>> citiesIdsToStudentsList = new HashMap<>();
for (Student student : students) {
    for (State state : student.getStates()) {
        for (City city : state.getCities()) {
            citiesIdsToStudentsList.computeIfAbsent(city.getId(), k -> new ArrayList<>()).add(student);
        }
    }
}

这里利用了computeIfAbsent方法来填充Map,并创建了一个具有相同城市id的每个学生的列表。


为了确保,嵌套的for循环解决方案是否等同于:students.forEach(student-> student.getStates(). forEach(state-> state.getCities(). forEach(city-> citiesIdsToStudentsList.computeIfAbsent(city.getId(),k-> new ArrayList <>())。add(student)))))? - srborlongan
1
@srborlongan 是的,在这种情况下它会有相同的结果。 - Tunaki

0

除了Tunaki的答案,你还可以将其简化为

Map<Integer, List<Student>> citiesIdsToStudentsList =
    students.stream()
        .flatMap(student -> student.getStates().stream()
            .flatMap(state -> state.getCities().stream())
            .map(state -> new AbstractMap.SimpleEntry<>(student, state.getId())))
        .collect(Collectors.groupingBy(
            Map.Entry::getValue,
            Collectors.mapping(Map.Entry::getKey, Collectors.toList())
        ));

它利用了您实际上并不关心 State 对象的事实,因此如果您在第一个 flatMap 操作中正确地执行,则可以直接将它们转换为所需的 City 对象。然后,通过在创建 Map.Entry 时立即执行 State.getId 操作,您可以简化实际的 collect 操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接