veruyi
veruyi

Reputation: 93

Collect a Stream of Map<K,V> to Map<K,List<V>>

I have a Stream< Map< K, V > > and I'm trying to merge those maps together, but preserve duplicate values in a list, so the final type would be Map< K, List<V> >. Is there a way to do this? I know the toMap collector has a binary function to basically choose which value is returned, but can it keep track of the converted list?

i.e.

if a is a Stream< Map< String, Int > >

a.flatMap(map -> map.entrySet().stream()).collect(
    Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (val1, val2) -> ??
);

Upvotes: 7

Views: 1785

Answers (2)

NoDataFound
NoDataFound

Reputation: 11949

Use groupingBy: see the javadoc, but in your case it should be something like that:

a.flatMap(map -> map.entrySet().stream())
 .collect(
   Collectors.groupingBy(
     Map.Entry::getKey, 
     HashMap::new, 
     Collectors.mapping(Map.Entry::getValue, toList())
   )
);

Or:

a.map(Map::entrySet).flatMap(Set::stream)
 .collect(Collectors.groupingBy(
     Map.Entry::getKey, 
     Collectors.mapping(Map.Entry::getValue, toList())
   )
);

Upvotes: 11

Patrick Parker
Patrick Parker

Reputation: 4959

This is a bit wordier than the groupingBy solution, but I just wanted to point out that it is also possible to use toMap (as you initially set out to do) by providing the merge function:

    a.flatMap(map -> map.entrySet().stream()).collect(
        Collectors.toMap(Map.Entry::getKey,
                entry -> { 
                    List<Integer> list = new ArrayList<>();
                    list.add(entry.getValue());
                    return list;
                },
                (list1, list2) -> {
                    list1.addAll(list2);
                    return list1;
                }));

Upvotes: 0

Related Questions