I have two maps of arrays.
Map<String, List<String>> map1 = new HashMap<>();
Map<String, List<String>> map2 = new HashMap<>();
I want to merge them in one new map.
If a key exists in both maps, in that case, I should merge arrays.
For example:
map1.put("k1", Arrays.asList("a0", "a1"));
map1.put("k2", Arrays.asList("b0", "b1"));
map2.put("k2", Arrays.asList("z1", "z2"));
// Expected output is
Map 3: {k1=[a0, a1], k2=[b0, b1, z1, z2]}
I tried to do that with streams
Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().stream().collect(Collectors.toList())
));
This work if there are no the same keys in maps. Otherwise, I get the exception
Exception in thread "main" java.lang.IllegalStateException: Duplicate key k2 (attempted merging values [b0, b1] and [z1, z2])
at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:133)
at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:180)
at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.base/java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.java:1751)
at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:274)
at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at im.djm.Test.main(Test.java:25)
Is there a way to accomplish this task with streams?
Or I have to iterate throug maps?
Duplicate keys are not allowed in a Map. Basically, Map Interface has two implementation classes HashMap and TreeMap the main difference is TreeMap maintains an order of the objects but HashMap will not. HashMap allows null values and null keys. Both HashSet and HashMap are not synchronized.
You can use a TreeMap with a custom Comparator in order to treat each key as unequal to the others. It would also preserve the insertion order in your map, just like a LinkedHashMap. So, the net result would be like a LinkedHashMap which allows duplicate keys!
If you try to insert the duplicate key, it will replace the element of the corresponding key. HashMap is similar to HashTable, but it is unsynchronized. It allows to store the null keys as well, but there should be only one null key object and there can be any number of null values.
HashMap does not allow duplicate keys however it allows to have duplicate values.
In this case, we can take the help of Map.merge () function added in Java 8. The merge () function takes 3 arguments: key, value and a user-provided BiFunction to merge values for duplicate keys. In our example, we want to append the values (from both maps) for a duplicate key. Program output. Notice the value of key "4".
Map.merge () Java 8 adds a new merge () function into the java.util.Map interface. Here is how the merge () function works: If the specified key is not already associated with a value or the value is null, it associates the key with the given value. Otherwise, it replaces the value with the results of the given remapping function.
If we want to handle the cases where duplicate keys are present in the maps and we do not want to loose the data for any map and for any key. In this case, we can take help of HashMap.merge () function added in Java 8. merge () function 3 arguments. Key, value and uses a user-provided BiFunction to merge values for duplicate keys.
Learn to merge two hashmaps in Java. We will learn to join hashmaps in two cases – ignore duplicate keys (overwrite values) or handle duplicate keys using Java 8. This one is simple solution. Simply use HashMap.putAll(HashMap) method which copies all of the mappings from the second map to first map. As we know hashmap does not allow duplicate keys.
Use a merge function in the case of duplicate keys:
Map<String, List<String>> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> new ArrayList<>(e.getValue()),
(left, right) -> {left.addAll(right); return left;}
));
Note, I've changed e -> e.getValue().stream().collect(Collectors.toList())
to new ArrayList<>(e.getValue())
to guarantee that we always have a mutable list which we can add into in the merge function.
Maybe. But you are more likely to get everything right by combining the entries manually, using iteration. I don't know if anyone else will have to work on this code, but they will likely be grateful for an easy to read approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With