Merging multiple maps in Java 8
Solution 1
One way to do it would be to create a stream of sets of entries and then flatmap it to have a stream of entries and collect those into a map.
Map<String,Animal> animals =
Stream.of(dogs.entrySet(), cats.entrySet(), elephants.entrySet())
.flatMap(Set::stream)
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
//Stream.of(dogs, cats, elephants).flatMap(m -> m.entrySet().stream()) could also be an option
Also not a one-liner and without streams using Map#putAll
:
Map<String,Animal> animals = new HashMap<>(dogs);
animals.putAll(cats);
animals.putAll(elephants);
Solution 2
You can accomplish the task at hand with the use of Stream.Concat
:
Map<String,Animal> animals = Stream.concat(Stream.concat(dogs.entrySet().stream(), cats.entrySet().stream()), elephants.entrySet().stream())
.collect(Collectors.toMap
(
Map.Entry::getKey,
Map.Entry::getValue
)
);
You'll need to be cautious here because when merging and there are duplicate keys then an exception will be raised as expected.
Solution 3
The best way IMO is as in Alexis C.'s answer, with Map.putAll
:
Map<String, Animal> animals = new HashMap<>(dogs);
animals.putAll(cats);
animals.putAll(elephants);
A variant on this one:
Map<String, Animal> animals = new HashMap<>(dogs);
cats.forEach(animals::put);
elephants.forEach(animals::put);
Related videos on Youtube
smeeb
Updated on June 04, 2022Comments
-
smeeb almost 2 years
Java 8 here. I have the following classes:
public interface Animal { ... } public class Dog implements Animal { ... } public class Cat implements Animal { ... } public class Elephant implements Animal { ... }
I have to implement the following method:
void doSomething(Map<String,Dog> dogs, Map<String,Cat> cats, Map<String,Elephant> elephants) { // TODO: // * Merge all dogs, cats & elephants together into the same Map<String,Animal>, // but... // * Do so generically (without having to create, say, a HashMap instance, etc.) }
In my
doSomething(...)
method, I need to merge all the map arguments into the sameMap<String,Animal>
map, but I'd really prefer to do so without my code having to instantiate a specific map implementation (such asHashMap
, etc.).Meaning, I know I could do this:
void doSomething(Map<String,Dog> dogs, Map<String,Cat> cats, Map<String,Elephant> elephants) { Map<String,Animal> animals = new HashMap<>(); for(String dog : dogs.keySet()) { animals.put(dog, dogs.get(dog)); } for(String cat : cats.keySet()) { animals.put(cat, cats.get(cat)); } for(String elephant : elephants.keySet()) { animals.put(elephant, elephants.get(elephant)); } // Now animals has all the argument maps merged into it, but is specifically // a HashMap... }
I'm even fine using some utility if it exists, like maybe a
Collections.merge(dogs, cats, elephants)
, etc. Any ideas?-
shmosel over 6 yearsWhy are you trying to avoid instantiating a new map?
-
smeeb over 6 yearsThanks @shmosel but not looking for an XY Answer here.
-
shmosel over 6 yearsI'm actually trying to understand the requirement. Is it not to create a new object? Not to use a particular implementation? To use the same implementation as the arguments? To support various implementations? It's not very clear what your goal is. Note that all the answers provided so far do, in fact, instantiate a new map.
-
-
smeeb over 6 yearsThanks @Aomine (+1) - but your code yields the following error: "Non-static method cannot be referenced from a static context"...any ideas?
-
Ousmane D. over 6 yearsthat's most likely because you're calling the method
doSomething
inside a static context. that's nothing to do with the solution at hand. 2 ways to solve it. 1 is to instantiate an object of the containing type and then call thedoSomething
from the new instance or make thedoSomething
method static. -
smeeb over 6 yearsOk, however
doSomething(...)
is not a static method. I'll start playing around with it a little. -
smeeb over 6 yearsYeah I'm following you, still something else is going on. This is a compiler (not runtime) error, and this is a library I'm building (so I don't have any other place in the code where
doSomething(...)
is explicitly called). -
smeeb over 6 yearsThanks @Federico but that still requires me to instantiate
animals
as aHashMap
. It sounds like their are other (stream-based) ways to avoid this so I'm more interested in those. -
smeeb over 6 yearsThanks @Alexis C (+1) but where is your
toMap(...)
static method coming from? -
shmosel over 6 years@smeeb
java.util.stream.Collectors
-
shmosel over 6 yearsI would make a small improvement:
Stream.of(dogs, cats, elephants).map(Map::entrySet)
-
Alexis C. over 6 years@shmosel But then you'll need to flatmap the stream i.e
Stream.of(dogs, cats, elephants).map(Map::entrySet).flatMap(Set::stream)
. In this case, I prefer to writeStream.of(dogs, cats, elephants).flatMap(m -> m.entrySet().stream())
directly, but matter of taste I guess. -
shmosel over 6 years@smeeb It's undocumented, but
toMap()
also produces aHashMap
. -
Alexis C. over 6 years@smeeb From the
Collectors
class. If you want to have a specific map implementation, you can use this overloaded method.