43

How can I flatten a Stream of Maps (of the same types) to a single Map in Java 8?

Map<String, Long> toMap(Stream<Map<String, Long>> stream) {
    return stream. ???
}
Eran
  • 387,369
  • 54
  • 702
  • 768
Dariusz Mydlarz
  • 2,940
  • 6
  • 31
  • 59

2 Answers2

64

My syntax may be a bit off, but flatMap should do most of the work for you :

Map<String, Long> toMap(Stream<Map<String, Long>> stream) {
    return stream.flatMap (map -> map.entrySet().stream()) // this would create a flattened
                                                           // Stream of all the map entries
                 .collect(Collectors.toMap(e -> e.getKey(),
                                           e -> e.getValue())); // this should collect
                                                               // them to a single map
}
Eran
  • 387,369
  • 54
  • 702
  • 768
  • The method stream() is undefined for the type Map – Dariusz Mydlarz Nov 05 '14 at 08:56
  • @DariuszMydlarz I fixed the answer. Use map.entrySet().stream() instead. – Eran Nov 05 '14 at 08:57
  • 14
    You can use method references: `Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)` (or if you import the nested class: `Collectors.toMap(Entry::getKey, Entry::getValue)`) – Holger Nov 05 '14 at 10:50
  • 11
    Note that the 2-parameter `toMap(...)` will throw an exception if the same key occurs more than once. If that's not what you want, use the 3-parameter `toMap(...)`. Use `(a,b)->b` as the third parameter to emulate `Map::addAll` (later values overwrite previous ones). – Misha Nov 05 '14 at 11:01
  • This doesn't work if values are null. Also if maps have conflicting keys there will be merge exception. Method given here is better http://stackoverflow.com/questions/24630963/java-8-nullpointerexception-in-collectors-tomap – Aman Nov 17 '16 at 10:58
1

I would like to propose a solution using reduce(), which is more intuitive for me. I would use it inline though.

Map<String, Long> toMap(Stream<Map<String, Long>> stream) {
    return stream.reduce(new HashMap<>(), Util::reduceInto);
}

And in Util.java:

public static <R, T> Map<R, T> reduceInto(Map<R, T> into, Map<R, T> valuesToAdd) {
    reduceInto.putAll(valuesToAdd);
    return reduceInto;
}

In this case reduceInto() works for any type of map and uses mutability to avoid creating a new Map for each item of the Stream.

Important: although this method allows repeated keys in the stream, reduceInto() is not associative, meaning that if you have repeated keys there is no guarantee of which will be the final value.

Brujua
  • 71
  • 1
  • 6