Reputation: 1269
I have a collection of maps. The internal maps can have keys that match, so I'd like to turn it into a Map
of Collection
:
Collection<Map<String, Thing>>
=> Map<String, Collection<Thing>>
What I've tried was:
Map<String, Collection<Thing>> newMap = oldCollection
.stream()
.map(Map::entrySet)
.collect(Collectors.groupingBy(
Entry::getKey,
Collectors.mapping(Entry::getValue, Collectors.toList())));
I feel as though this should work but I get a compilation error:
Type mismatch: cannot convert from Map<Object,List<Object>> to Map<String,Collection<Thing>>
Does anyone know what I'm doing wrong?
Upvotes: 2
Views: 4963
Reputation: 425073
It could be simpler to create the map first:
Map<String, Collection<Thing>> map = new HashMap<>();
oldCollection.stream()
.map(Map::entrySet)
.flatMap(Set::stream)
.forEach(e -> map.computeIfAbsent(e.getKey(), k -> new ArrayList<>()).add(e.getValue()));
Upvotes: 0
Reputation: 21124
Alternatively you can do it like so,
final Map<String, List<Thing>> thingsMap = oldCollection.stream()
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(
Map.Entry::getValue,
Collectors.toList()
)
));
You can get the entry set of each map, there you get a set of Sets, and then you flatten them to get one large Stream out of them. Finally you process that large stream.
Upvotes: 1
Reputation: 298233
For comparison, this is the loop solution:
Map<String, Collection<Thing>> newMap = new HashMap<>();
for(Map<String, Thing> m: oldCollection)
m.forEach((s,t) -> newMap.computeIfAbsent(s, x->new ArrayList<>()).add(t));
You could express the same logic as Stream operation:
Map<String, Collection<Thing>> newMap = oldCollection
.stream()
.collect(HashMap::new,
(r,m)->m.forEach((s,t)->r.computeIfAbsent(s,x->new ArrayList<>()).add(t)),
(r,m)->m.forEach((s,l)->r.computeIfAbsent(s,x->new ArrayList<>()).addAll(l)));
This is what the other solution also do, except that the logic of flatMap
has been integrated into the collector.
Upvotes: 1
Reputation: 393846
oldCollection.stream().map(Map::entrySet)
creates a Stream<Set<Map.Entry<String,Thing>>>
, but you need a Stream<Map.Entry<String,Thing>>
.
Therefore you need to use flatMap
:
Map<String, List<Thing>>
newMap = oldCollection.stream()
.flatMap(m->m.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
Also, since you are mapping the grouped values to a List
, the output type should be Map<String, List<Thing>>
.
You can change Collectors.toList()
to Collectors.toCollection(ArrayList::new)
if you want to keep the current output type.
Upvotes: 4