Reputation: 2164
Im trying to do something seemingly straighforward but with no luck so far. I have a list of Map, they come from a bunch of CompletableFutures in parallel, the result then has to be joined. Since the key in the map is unique, In need to merge the values and produce a new map of unique keys associated with the combined list of SomeType. For example:
Map1: key1 : Sometype1 key2 : Sometype2
Map2 key1 : Sometype3 key2 : Sometype4
Desired final result: Map3: key1 : Sometype, Sometype3 key2 : Sometype2, Sometype4
I tried using groupBy and other methods, but it seems most of them assume working with a List and not a Map to begin with so I wasn't able to find a straightforward way to do it.
So far I have something like:
Map<String, List<Sometype>> volPercent = waitGroup.stream()
.map(CompletableFuture::join)
.flatMap((e) -> e.entrySet().stream())
.collect(Collectors.groupingBy());
The part I'm not sure is what needs to go in the grouping by part. I tried Map.Entry::getKey, but it doesn't compile:
Error:(69, 25) java: incompatible types: inference variable T has incompatible bounds
equality constraints: com.Sometype
lower bounds: java.util.Map.Entry<java.lang.String,com.Sometype>
Upvotes: 9
Views: 20404
Reputation: 132360
I'll assume that you're starting with a stream of maps:
Stream<Map<String, Sometype>> input = ... ;
You got pretty far with the flatmapping and collecting. However, the code you tried,
input.flatMap(map -> map.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey));
gives you a map whose keys are String
and whose values are a list of map entries, specifically List<Map.Entry<String,Sometype>>
, not a List<Sometype>
as you wanted. What you have to do is use the "downstream" feature of collectors after you've grouped them by key. You want to take each Map.Entry
and extract (map) it to its value, which is Sometype
. This is done with a mapping
collector.
Now you potentially have several instances of Sometype
for each key, so you need to collect them into a list. This is done using another downstream collector, which collects them into a list. This is done using the familiar toList()
collector.
The final code looks like this:
Map<String, List<Sometype>> result =
input.flatMap(map -> map.entrySet().stream())
.collect(groupingBy(Map.Entry::getKey,
mapping(Map.Entry::getValue,
toList())));
Upvotes: 14
Reputation: 37845
The problem is that your initial attempt (as I understand it)
Collectors.groupingBy(Map.Entry::getKey)
collects to a Map<K, List<Map.Entry<K, V>>>
.
After the flatMap
, you have a Stream<Map.Entry<K, V>>
whose elements groupingBy
maps to K
.
But you want a Map<K, List<V>>
so you need to use the groupingBy
that takes a downstream collector, not just a classifier, so you can also map Map.Entry<K, V>
to V
.
Something like
Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(
Map.Entry::getValue,
Collectors.toList()
)
)
Upvotes: 5