Merging two Maps in Java

Learn to merge or join two Maps in Java using Java Stream API and Plain Java ways. We’ll also cover how to resolve the duplicate key problem.

Overview

This is a quick and example-oriented tutorial on Joining or Merging two HashMaps in Java. We will cover various ways of merging Maps offered by Java 8 Streams API and plain Java.

Java Maps stores elements based on the keys. Thus, when we reinsert an existing key with a different value, the existing value gets replaced. However, during the merge, the key collision may produce IllegalStateException. We’ll cover how we can provide custom value mappers to avoid the exception.

Merging or Joining Two Maps

Before we see different ways of combining key and value pairs between two Java Maps, let’s first create two Java Maps.

Here, we are using Java Map Inline Initialization two create two HashMaps.

Map<Integer, String> map1 = Map.of(
    1, "Ned",
    2, "Jon",
    3, "Khal"
);

Map<Integer, String> map2 = Map.of(
    1, "Tywin",
    2, "Jon",
    4, "Petyr"
);Code language: Java (java)

Comparing the entries between the maps, the first entry of the second Map has a different value. The second entry has the same value, while the third entry is new.

Java Streams

This section will use Java Streams API to combine two maps. As mentioned previously, we will also understand the duplicate key problem and a way to solve that.

Using Stream.of()

The of() method of Stream creates a new stream of given elements. We’ll use it to create a stream of two maps and then use the flatMap() method to create a combined stream of their entry objects.

Map<Integer, String> map3 = Stream.of(map1, map2)
    .flatMap(map -> map.entrySet().stream())
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        Map.Entry::getValue));Code language: Java (java)

At first, we created a Stream of two Maps instances and used the flatMap() method to create a combined stream of their Entry elements. We used a Java Stream Collector toMap() method to collect the key and value pairs from the Stream as a new Java Map.

Duplicate Keys can cause IllegalStateException

However, given that our maps contain the same key with different values, the above merge operation will fail with an IllegalStateException.

Exception in thread "main" java.lang.IllegalStateException: 
Duplicate key 1 (attempted merging values Ned and Tywin)Code language: plaintext (plaintext)

To solve this duplicate key problem, we will have to provide a merge function that is of BinaryOperator type.

public static <T, K, U>
  Collector<T, ?, Map<K,U>> toMap(
      Function<? super T, ? extends K> keyMapper,
      Function<? super T, ? extends U> valueMapper,
      BinaryOperator<U> mergeFunction)Code language: JavaScript (javascript)

As shown in the toMap() method definition, we can provide a merge function as the third argument.

How to solve the IllegalStateException caused by Duplicate Key problem.

Map<Integer, String> map3 = Stream.of(map1, map2)
    .flatMap(map -> map.entrySet().stream())
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        Map.Entry::getValue,
        (value1, value2) -> value1));

System.out.println(map3);
//prints:
//{1=Ned, 2=Jon, 3=Khal, 4=Petyr}
Code language: Java (java)

In the merge function, we chose the value from the first Map that resolves the exception.

Using Stream.concat()

Alternatively, we can use the concat() function of the Stream to merge two maps. This function can combine two different streams into one.

Map<Integer, String> map3 = 
  Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
    .collect(Collectors.toMap(
        Map.Entry::getKey, 
        Map.Entry::getValue, 
        (v1, v2) -> v1));Code language: Java (java)

As shown in the snippet, we passed the streams of map1 and map2 to the concate() function and then collected the Stream of their combined entry elements. Similar to the previous example, we provided a merge function to specify our merging strategy.

Plain Java

Java without streams also offers a couple of ways to merge Java maps. Let’s go over them quickly.

Using Map.putAll()

The putAll() method of the Java Map interface dumps all the entries of the given Map into this Map. We can use the putAll() method to merge two Java HashMaps into one.

Map<Integer, String> map3 = new HashMap<>();
map3.putAll(map1);
map3.putAll(map2);

System.out.println(map3);
//prints:
//{1=Tywin, 2=Jon, 3=Khal, 4=Petyr}Code language: Java (java)

We created an empty HashMap and then used the putAll() method to dump all the entries from the first and the second Map.

In case of duplicate keys, the putAll() method replaces any existing occurrence of the key. Thus, we can see the first entry of the resulting Map has the value from the second Map. If we want the value from the first Map, we can add the second Map before the first.

Using Map.merge()

The merge() method of Java Map adds the given key-value pair in this Map. Thankfully, this method allows us to provide a merging strategy that it uses to resolve the duplicate keys.

default V merge(
  K key, 
  V value, 
  BiFunction<? super V,? super V,? extends V> remappingFunction)Code language: Java (java)

The given value is used if the given key doesn’t exist or has a null value in the current Map. However, if the given key already exists in the current Map, it uses the merging strategy provided by the remapping function.

Map<Integer, String> map3 = new HashMap<>(map1);
  map2.forEach((key, value) ->
    map3.merge(
      key,
      value,
      (value1, value2) -> value1));

System.out.println(map3);
//prints:
//{1=Ned, 2=Jon, 3=Khal, 4=Petyr}Code language: Java (java)

We created a new HashMap instance using all the entries of the first Map. Then we used the merge() method to merge all the entries from the second Map one after the other. Note that our remapping function instructs it to pick the value from the first Map.

Summary

This quick tutorial covered different ways of merging two Map instances in Java. We used Java Stream API and several plain Java methods to join two Maps to create a Map of their elements merged. Also, we understood the reasoning behind the IllegalStateException that we get while merging two maps with duplicate keys. We handled the duplicate key problem by providing a custom merging strategy.

For the complete source of the examples used here, please visit our GitHub Repository.

Related Posts: