Convert Map of maps into Lists using Java 8 Streams

我与影子孤独终老i 提交于 2019-12-09 19:43:22

问题


I have a map:

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

Outcome:
List<Id>
List<ReferenceId>

I wanna convert this map into two list of Integers. One list contains inner-map keys, and other contains inner-map value (i.e. List<Integer>)

Can anyone tell me how to do this in Java 8 using streams?

I tried this way but got Cast Exception, can not convert String to Integer.

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

回答1:


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>



回答2:


There is no way, how your code

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());

can produce a ClassCastException, unless you managed to insert objects of wrong type into the source map via unchecked operation(s) before the Stream operation. Such a situation is called heap pollution and you should compile your entire code with all warnings enabled (javac: use option -Xlint:unchecked) and solve them.

But note that your code is unnecessarily complicated. The chain, .entrySet().stream().map(e -> e.getKey()) is streaming over the entries and mapping to the keys, so you can stream over the keys in the first place, i.e. .keySet().stream(). Then, you are collecting the stream into a List, just to invoke .stream() in the subequent flatMap step, so you can simply use the stream you already have instead:

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

Alternatively, you can let the collector do all the work:

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

Getting the values instead of the keys works similar, but requires another flatMap step to get the List elements:

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

which is equivalent to

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

Again, there’s the alternative of letting the collector do all the work:

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

or

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



回答3:


If your value is like Map<String,Object>. And your Object is like 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())

it works




回答4:


Adding for one more working scenario

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

Retrieving logic

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

Result will be

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



来源:https://stackoverflow.com/questions/38360276/convert-map-of-maps-into-lists-using-java-8-streams

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!