Java流 - 通过嵌套列表分组(按第二顺序列出)

时间:2016-04-30 21:03:03

标签: java java-8 java-stream

我有以下数据结构 -

每个学生列出各自拥有城市列表的州的列表。

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 个答案:

答案 0 :(得分:5)

使用Stream API,您需要平面地图两次,并将每个中间学生和城市映射到一个能够抓住学生的元组。

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填充地图,并创建具有相同城市ID的每个学生的列表。

答案 1 :(得分:0)

Tunaki’s answer外,您还可以将其简化为

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它们可以直接flatMap。 {1}}操作。然后,通过在创建State.getId时立即执行Map.Entry操作,您可以简化实际的collect操作。