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

use*_*165 2 java java-stream

我有一张地图:

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

Outcome:
List<Id>
List<ReferenceId>
Run Code Online (Sandbox Code Playgroud)

我想将这个地图转换成两个整数列表.一个列表包含内部映射键,另一个列表包含内部映射值(即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());
Run Code Online (Sandbox Code Playgroud)

shm*_*sel 8

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>
Run Code Online (Sandbox Code Playgroud)


Hol*_*ger 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());
Run Code Online (Sandbox Code Playgroud)

可以产生一个ClassCastException,除非您设法在Stream操作之前通过未经检查的操作将错误类型的对象插入到源映射中。这种情况称为堆污染,您应该在启用所有警告的情况下编译整个代码(javac:use选项-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());
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

相当于

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

同样,也可以让收集器完成所有工作:

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

要么

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

  • @Jean-FrançoisSavard:这基本上可以归结为以下问题:目标集合是否可以通过使用addAll而不是重复的add来获得好处,例如ArrayList将确保整个集合的容量足够。从理论上讲,`toList`收集器可以使用中间数据结构,从而避免了增加容量的操作成本,但是今天,这没有发生。类似地,`flatMap`可以允许并行流中更多的并发,但是在当前实现中不可行。因此,今天,在收集器中完成所有工作很可能会取胜。 (2认同)