smeeb
smeeb

Reputation: 29487

Merging multiple maps in Java 8

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 same Map<String,Animal> map, but I'd really prefer to do so without my code having to instantiate a specific map implementation (such as HashMap, 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?

Upvotes: 10

Views: 20832

Answers (3)

Alexis C.
Alexis C.

Reputation: 93842

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);

Upvotes: 15

fps
fps

Reputation: 34460

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);

Upvotes: 1

Ousmane D.
Ousmane D.

Reputation: 56423

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.

Upvotes: 2

Related Questions