Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge map of arrays with duplicate keys

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?

like image 295
djm.im Avatar asked May 16 '18 20:05

djm.im


People also ask

Does Map accept duplicate keys?

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.

How do I allow duplicate keys on a Map?

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!

What happens if we enter duplicate key in Map?

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.

Can HashMap contain duplicate values?

HashMap does not allow duplicate keys however it allows to have duplicate values.

How to merge maps for duplicate keys in Java?

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".

What is map merge () in Java 8?

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.

How to handle duplicate keys in HashMap in Java 8?

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.

How to merge two hashmaps in Java 8?

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.


2 Answers

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.

like image 134
Ousmane D. Avatar answered Sep 30 '22 08:09

Ousmane D.


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.

like image 45
Steve11235 Avatar answered Sep 30 '22 08:09

Steve11235