Merge Map <String, List <String> Java 8 Stream
I would like to combine two cards 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 am trying 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 produces the result:
Map<String, List<Object>>
If one key is not contained in mapGlobal , it will be added as a new key with the corresponding String list. If the key is duplicated in mapGlobal and mapAdded , both lists of values will be combined as: A = {1, 3, 5, 7} and B = {1, 2, 4, 6} , then A ∪ B = {1, 2, 3, 4, 5, 6, 7} .
You can do this by mapAdded through all the entries in mapAdded and merging them into mapGlobal .
Next, iterates through the mapAdded entries by calling forEach(action) where the action uses the key and value of each entry. For each record, we call merge(key, value, remappingFunction) for mapGlobal : this will either create a record under the key k and a value v if the key does not exist, or it will call the specified remapping function if they already existed. This function combines 2 lists, which in this case are first added to the TreeSet to ensure uniqueness and sorting of elements and conversion back to the 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 this potentially in parallel, you can create a stream pipeline by getting entrySet() into it and calling the parallelStream() functions. But then you need to make sure that you are using a map that supports concurrency for mapGlobal , such as 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); })); Using foreach on top of Map can be used to concatenate a given array.
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; } The original implementation does not produce a result of type Map<String, List<Object>> , but Map<String, List<List<String>>> . You will need an additional Stream pipeline to create a Map<String, List<String>> .