使用Java 8 Streams将地图映射转换为列表

时间:2016-07-13 19:36:26

标签: java java-stream

我有一张地图:

Map<String, Map<Integer, List<Integer>>>
e.g. Map<Name, Map<Id, List<ReferenceId>>>

Outcome:
List<Id>
List<ReferenceId>

我想将此地图转换为两个整数列表。一个列表包含内部映射键,另一个列表包含内部映射值(即List<Integer>

有人能告诉我如何使用流在Java 8中执行此操作吗?

我试过这种方式但得到了Cast Exception,无法将String转换为Integer。

map.values().stream()
    .map(m -> m.entrySet()
    .stream()
    .map(e -> e.getKey())
    .collect(Collectors.toList()))
    .flatMap(l -> l.stream())
    .collect(Collectors.toList());

4 个答案:

答案 0 :(得分:5)

Map<String, Map<Integer, List<Integer>>> map = ...

List<Integer> keys = map.values()       // Collection<Map<Integer, List<Integer>>>
        .stream()                       // Stream<Map<Integer, List<Integer>>>
        .map(Map::keySet)               // Stream<Set<Integer>>
        .flatMap(Set::stream)           // Stream<Integer>
        .collect(Collectors.toList());  // List<Integer>

List<Integer> values = map.values()     // Collection<Map<Integer, List<Integer>>>
        .stream()                       // Stream<Map<Integer, List<Integer>>>
        .map(Map::values)               // Stream<Collection<List<Integer>>>
        .flatMap(Collection::stream)    // Stream<List<Integer>>
        .flatMap(List::stream)          // Stream<Integer>
        .collect(Collectors.toList());  // List<Integer>

答案 1 :(得分:5)

没有办法,你的代码如何

List<Integer> list = map.values().stream()
    .map(m -> m.entrySet().stream()
            .map(e -> e.getKey())
            .collect(Collectors.toList()))
    .flatMap(l -> l.stream())
    .collect(Collectors.toList());

可以生成ClassCastException,除非您在 Stream操作之前通过未经检查的操作设法将错误类型的对象插入到源地图中。这种情况称为堆污染,您应该在启用所有警告(javac:使用选项-Xlint:unchecked)的情况下编译整个代码并解决它们。

但请注意,您的代码不必要地复杂化。链.entrySet().stream().map(e -> e.getKey())在条目上流式传输并映射到键,因此您可以首先在键上流式传输,即.keySet().stream()。然后,您将流收集到List,只是在次要.stream()步骤中调用flatMap,因此您只需使用您已有的流:

List<Integer> list = map.values().stream()
    .flatMap(m -> m.keySet().stream())
    .collect(Collectors.toList());

或者,您可以让收集者完成所有工作:

List<Integer> list = map.values().stream()
    .collect(ArrayList::new, (l,m) -> l.addAll(m.keySet()), List::addAll);

获取值而不是键的工作方式类似,但需要另一个flatMap步骤来获取List元素:

List<Integer> list = map.values().stream()
    .flatMap(m -> m.values().stream().flatMap(List::stream))
    .collect(Collectors.toList());

相当于

List<Integer> list = map.values().stream()
    .flatMap(m -> m.values().stream())
    .flatMap(List::stream)
    .collect(Collectors.toList());

同样,还有让收集者完成所有工作的替代方案:

List<Integer> list = map.values().stream()
    .collect(ArrayList::new, (l,m)->m.values().forEach(l::addAll), List::addAll);

List<Integer> list = map.values().stream()
    .collect(ArrayList::new, (l,m)->m.forEach((k,v)->l.addAll(v)), List::addAll);

答案 2 :(得分:1)

如果您的值类似于Map<String,Object>。您的Object就像Map<String,Object>

 Set<String> mapKeys = myMap.entryset()    //Set<Entry<String,Object>>
.stream()                                  //Stream<Entry<String,Object>>
.map(m->(Map<String,Object>) m.getValue()) //Stream<Map<String,Object>>
.map(Map::keySet)                          //Stream<Set<String>>
.flatMap(l->l.stream())                    //Stream<String>
.collect(Collectors.toSet())

有效

答案 3 :(得分:0)

再增加一个工作场景

 Map<Integer, List<Integer>> existingPacakagesMap = // having value {123=[111, 222, 333], 987=[444, 555, 666]}

检索逻辑

 List<Integer>   ListOfAllPacakages= existingPacakagesMap.values().stream().flatMap(List::stream).collect(Collectors.toList());

结果将是

  

ListOfAllPacakages = [111,222,333,444,555,666]