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

我有以下数据结构 –

每个学生的名单,每个学生都拥有一个国家名单,每个国家都有一份城市名单。

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

我想得到以下内容。

 Map citiesIdsToStudensList; 

我写了以下内容

 Map<Integer, List> 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()))); 

但它并没有让我得到我想要的结果。

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

 Map> 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> 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的每个学生的列表。

除了Tunaki的答案 ,你可以简化它

 Map> 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操作中正确执行它,则可以将它们直接flatMap到所需的City对象。 然后,通过在创建Map.Entry时立即执行State.getId操作,可以简化实际的collect操作。