Alex Pritchard Alex Pritchard - 5 months ago 10
Java Question

Use streams to group Map<Foo,List<Bar>> based on size of the List<Bar>

I'm trying to transform data of this format:

Map<Foo, List<Bar>> mapFooToBars; //start Map
Map<Integer, List<Foo>> mapListSizeToFoos //destination Map


I'd like to create a grouping based on the size of the
List<Bar>
for each entry, so that in my output map, I would have a list of all Foos that have 0 Bars together, all Foos that have 1 Bar together, and so on.

I was trying to do this:

Map<Foo, List<Bar>> fooBarMap = whatever();
fooBarMap.entrySet().stream()
.collect(Collectors.groupingBy(entry -> entry.getValue().size()));
//expected Map<Integer,Entry<Foo, List<Bar>> which
//could be transformed into Map<Integer,Foo> pretty easily


I apparently am unclear on how groupingBy works exactly, as this is giving me a "getValue() is undefined for the type Object" error. Somewhere along the line I'm losing the type information about entrySet I guess? It made me wonder if this was not the best way to go about this. Any help appreciated!

Answer

You are on the right track. You should use groupingBy but with mapping(Map.Entry::getKey, toList()) as the second parameter. This will collect the keys into a list and those lists will be the values of the resulting map.

fooBarMap.entrySet().stream()
    .collect(groupingBy(
        e -> e.getValue().size(),
        mapping(Map.Entry::getKey, toList())
    ));

above assumes static imports of various collectors