21

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

Tunaki
138k46 gold badges367 silver badges443 bronze badges
asked Mar 24, 2016 at 22:59
0

6 Answers 6

23

You can do this by iterating over all the entries in mapAdded and merging them into mapGlobal.

The following iterates over the entries of mapAdded by calling forEach(action) where the action consumes the key and value of each entry. For each entry, we call merge(key, value, remappingFunction) on mapGlobal: this will either create the entry under the key k and value v if the key didn't exist or it will invoke the given remapping function if they already existed. This function takes the 2 lists to merge, which in this case, are first added to a TreeSet to ensure both unique and sorted elements and converted back into a list:

mapAdded.forEach((k, v) -> mapGlobal.merge(k, v, (v1, v2) -> {
 Set<String> set = new TreeSet<>(v1);
 set.addAll(v2);
 return new ArrayList<>(set);
}));

If you want to run that potentially in parallel, you can create a Stream pipeline by getting the entrySet() and calling parallelStream() on it. But then, you need to make sure to use a map that supports concurrency for mapGlobal, like a ConcurrentHashMap.

ConcurrentMap<String, List<String>> mapGlobal = new ConcurrentHashMap<>();
// ...
mapAdded.entrySet().parallelStream().forEach(e -> mapGlobal.merge(e.getKey(), e.getValue(), (v1, v2) -> {
 Set<String> set = new TreeSet<>(v1);
 set.addAll(v2);
 return new ArrayList<>(set);
}));
answered Mar 24, 2016 at 23:49

5 Comments

this implementation will not use the improvements in the stream collections. Both maps can be huge then I would like to use something like parallelStream(). Is that possible.
Unless mapGlobal is a ConcurrentMap, mutating it from inside a parallel stream is not safe.
@Misha Correct, hence the suggested use of a ConcurrentHashMap.
@Tunaki Well, -1 to me for reading comprehension. I apologize.
@Misha I understand :) and made an edit to make it clearer.
5

Using foreach over Map can be used to merge given arraylist.

 public Map<String, ArrayList<String>> merge(Map<String, ArrayList<String>> map1, Map<String, ArrayList<String>> map2) {
 Map<String, ArrayList<String>> map = new HashMap<>();
 map.putAll(map1);
 map2.forEach((key , value) -> {
 //Get the value for key in map.
 ArrayList<String> list = map.get(key);
 if (list == null) {
 map.put(key,value);
 }
 else {
 //Merge two list together
 ArrayList<String> mergedValue = new ArrayList<>(value);
 mergedValue.addAll(list);
 map.put(key , mergedValue);
 }
 });
 return map;
}
answered Nov 25, 2016 at 14:04

Comments

2

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

answered Sep 29, 2017 at 18:47

Comments

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

answered Apr 26, 2022 at 15:06

Comments

0

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))
answered Jan 22, 2020 at 14:33

Comments

0

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]}
João Dias
17.6k6 gold badges39 silver badges47 bronze badges
answered Dec 23, 2021 at 16:10

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.