Merge Map<String, List<String> Java 8 Stream

Viewed 28663

I would like to merge two Map with JAVA 8 Stream:

Map<String, List<String>> mapGlobal = new HashMap<String, List<String>>();
Map<String, List<String>> mapAdded = new HashMap<String, List<String>>();

I try to use this implementation:

mapGlobal = Stream.of(mapGlobal, mapAdded)
                .flatMap(m -> m.entrySet().stream())
                .collect(Collectors.groupingBy(Map.Entry::getKey,
                        Collectors.mapping(Map.Entry::getValue,        
                                           Collectors.toList())
                ));

However, this implementation only create a result like:

Map<String, List<Object>>

If one key is not contained in the mapGlobal, it would be added as a new key with the corresponding List of String. If the key is duplicated in mapGlobal and mapAdded, both list of values will be merge as: A = {1, 3, 5, 7} and B = {1, 2, 4, 6} then A ∪ B = {1, 2, 3, 4, 5, 6, 7}.

6 Answers

The original implementation doesn't create result like Map<String, List<Object>>, but Map<String, List<List<String>>>. You need additional Stream pipeline on it to produce Map<String, List<String>>.

Map<String, List<String>> result = new HashMap<String, List<String>>();

Map<String, List<String>> map1 = new HashMap<String, List<String>>();
Map<String, List<String>> map2 = new HashMap<String, List<String>>();

for(Map.Entry<String, List<String>> entry: map1.entrySet()) {
   result.put(entry.getKey(), new ArrayList<>(entry.getValue());
}

for(Map.Entry<String, List<String>> entry: map2.entrySet()) {
   if(result.contains(entry.getKey())){
       result.get(entry.getKey()).addAll(entry.getValue());
   } else {
       result.put(entry.getKey(), new ArrayList<>(entry.getValue());
   }

}

This solution creates independent result map without any reference to map1 and map2 lists.

Using StreamEx

Map<String, List<String>> mergedMap =
        EntryStream.of(mapGlobal)
                .append(EntryStream.of(mapAdded))
                .toMap((v1, v2) -> {
                    List<String> combined = new ArrayList<>();
                    combined.addAll(v1);
                    combined.addAll(v2);
                    return combined;
                });

If you have even more maps to merge just append to the stream

                .append(EntryStream.of(mapAdded2))
                .append(EntryStream.of(mapAdded3))

Here is the full code to Iterate Two HashMap which has values stored in the form of a list. Merging all the keys and values in first hashmap. Below is the example.

HashMap<String, List<String>> hmap1 = new HashMap<>();
      List<String> list1 = new LinkedList<>();
      list1.add("000");
      list1.add("111");
      List<String> list2 = new LinkedList<>();
      list2.add("222");
      list2.add("333");
      hmap1.put("Competitor", list1);
      hmap1.put("Contractor", list2);
      //  System.out.println(hmap1);


      HashMap<String, List<String>> hmap2 = new HashMap<>();
      List<String> list3 = new LinkedList<>();
      list3.add("aaa");
      list3.add("bbb");
      List<String> list4 = new LinkedList<>();
      list4.add("ccc");
      list4.add("ddd");
      hmap2.put("Competitor", list3);
      hmap2.put("Contractor", list4);


//******* Java 8 Feature *****
hmap1.forEach((k, v) -> hmap2.merge(k, v, (v1, v2) -> {
          List<String> li = new LinkedList<>(v1);
          li.addAll(v2);
          hmap2.put(k,li);
          return new ArrayList<>(li);
      }));
      System.out.println(hmap2);

Output:

{Competitor=[aaa, bbb, 000, 111], Contractor=[ccc, ddd, 222, 333]}
Related