17

ı am trying to merge more than one hashmaps also sum the values of same key, ı want to explain my problem with toy example as follows

    HashMap<String, Integer> m = new HashMap<>();
    HashMap<String, Integer> m2 = new HashMap<>();

    m.put("apple", 2);
    m.put("pear", 3);
    m2.put("apple", 9);
    m2.put("banana", 6);

ı tried putall

m.putAll(m2);

output is as follows {banana=6, apple=9, pear=3}

but its result is not true for this problem. ı want to output as

{banana=6, apple=11, pear=3}

how can ı get this result in java?

Melike Ttkn
  • 181
  • 1
  • 1
  • 5
  • ı rewrite wanted result hashmap more clearly as {banana=6, apple=9+2, pear=3} if more than one hashmaps have same keys, results will be sum of values . if other hashmaps hasnt got same key, value wont change and all keys in all hashmaps will be in result hashmaps with their values – Melike Ttkn Mar 04 '15 at 23:02
  • I love how each subsequent answer becomes longer and longer and Java experts communicate that they are better versions of the corresponding previous ones. Nice example to start learning the language and its community :) – EugZol Jul 30 '19 at 10:38

8 Answers8

34

If you are using Java 8, you can use the new merge method of Map.

m2.forEach((k, v) -> m.merge(k, v, (v1, v2) -> v1 + v2));
prunge
  • 22,460
  • 3
  • 73
  • 80
  • 1
    What about entries in `m` that don't occur in `m2`? – sprinter Mar 04 '15 at 23:07
  • `m` will contain the merged results, output from this code: `{banana=6, apple=11, pear=3}` – prunge Mar 04 '15 at 23:10
  • Ah I see - you're merging into original map rather than creating a new one. I hadn't realised that's what the OP intended but I see that now in his question. So it's essentially relying on the mutability of the original map. – sprinter Mar 04 '15 at 23:55
  • This can also be written as `m2.forEach((k, v) -> m.merge(k, v, Integer::sum));` or `Long::sum` or `Double::sum` or `Float::sum`, depending on what you're summing. – Paul Sep 25 '21 at 19:13
12

This is a very nice use case for Java 8 streams. You can concatentate the streams of entries and then collect them in a new map:

Map<String, Integer> combinedMap = Stream.concat(m1.entrySet().stream(), m2.entrySet().stream())
    .collect(Collectors.groupingBy(Map.Entry::getKey,
             Collectors.summingInt(Map.Entry::getValue)));

There are lots of nice things about this solution, including being able to make it parallel, expanding to as many maps as you want and being able to trivial filter the maps if required. It also does not require the orginal maps to be mutable.

sprinter
  • 27,148
  • 6
  • 47
  • 78
1

This method should do it (in Java 5+)

public static <K> Map<K, Integer> mergeAndAdd(Map<K, Integer>... maps) {
    Map<K, Integer> result = new HashMap<>();
    for (Map<K, Integer> map : maps) {
        for (Map.Entry<K, Integer> entry : map.entrySet()) {
            K key = entry.getKey();
            Integer current = result.get(key);
            result.put(key, current == null ? entry.getValue() : entry.getValue() + current);
        }
    }
    return result;
}
Lucas Ross
  • 1,049
  • 8
  • 17
1

Here's my quick and dirty implementation:

import java.util.HashMap;
import java.util.Map;

public class MapMerger {

    public static void main(String[] args) {
        HashMap<String, Integer> m = new HashMap<>();
        HashMap<String, Integer> m2 = new HashMap<>();

        m.put("apple", 2);
        m.put("pear", 3);
        m2.put("apple", 9);
        m2.put("banana", 6);

        final Map<String, Integer> result = (new MapMerger()).mergeSumOfMaps(m, m2);
        System.out.println(result);
    }

    public Map<String, Integer> mergeSumOfMaps(Map<String, Integer>... maps) {
        final Map<String, Integer> resultMap = new HashMap<>();
        for (final Map<String, Integer> map : maps) {
            for (final String key : map.keySet()) {
                final int value;
                if (resultMap.containsKey(key)) {
                    final int existingValue = resultMap.get(key);
                    value = map.get(key) + existingValue;
                }
                else {
                    value = map.get(key);
                }
                resultMap.put(key, value);
            }
        }
        return resultMap;
    }
}

Output:

{banana=6, apple=11, pear=3}

There are some things you should do (like null checking), and I'm not sure if it's the fastest. Also, this is specific to integers. I attempted to make one using generics of the Number class, but you'd need this method for each type (byte, int, short, longer, etc)

Zymus
  • 1,673
  • 1
  • 18
  • 38
1

ı improve Lucas Ross's code. in stead of enter map by one by in function ı give all maps one times to function with arraylist of hashmap like that

    public HashMap<String, Integer> mergeAndAdd(ArrayList<HashMap<String, Integer>> maplist) {
    HashMap<String, Integer> result = new HashMap<>();
    for (HashMap<String, Integer> map : maplist) {
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();
            Integer current = result.get(key);
            result.put(key, current == null ? entry.getValue() : entry.getValue() + current);
        }
    }
    return result;
}

}

it works too. thanks to everbody

Melike Ttkn
  • 181
  • 1
  • 1
  • 5
1

Assume that you have many HashMaps: Map<String,Integer> map1, map2, map3;

Then you can use Java 8 streams:

Map<String,Integer> combinedMap = Stream.of(map1, map2, map3)
                .flatMap(map -> map.entrySet().stream())
                .collect(Collectors.groupingBy(Map.Entry::getKey,
                        Collectors.summingInt(Map.Entry::getValue)));
Nano
  • 111
  • 1
  • 5
0

Something like this should work:

 for (Map.Entry<String, Integer> entry : map.entrySet()) {
    String map1_key = entry.getKey();
    int map1_value = entry.getValue();

    //check:
    if(map2.get(map1_key)!=null){
    int map2_value = map2.get(map1_key);
    //merge:
    map3.put(map1_key,map1_value+map2_value);
    }else{
    map3.put(map1_key,map1_value);
    }
}


  for (Map.Entry<String, Integer> entry2 : map2.entrySet()) {
        String map2_key = entry2.getKey();
        int map2_value = entry2.getValue();

        //check:
        if(map1.get(map2_key)!=null){
        int map1_value = map1.get(map2_key);
        //merge:
        map3.put(map2_key,map1_value+map2_value);
        }else{
        map3.put(map2_key,map2_value);
        }
    }
Petro
  • 3,484
  • 3
  • 32
  • 59
  • What if the key from map1 has no value in map2? And what about entries that only exist in map2? – fps Mar 04 '15 at 22:41
  • thanks but it is not my problem's solution exactly. if all maps have same key it will works but if there is different key from others it wont be work – Melike Ttkn Mar 04 '15 at 22:47
  • oh sorry, i thought you said you needed to combine the VALUES based off the same KEYS – Petro Mar 04 '15 at 22:48
0

If the key exists, add to it's value. If not insert.

Here is a simple example which merges one map into another:

Foo oldVal = map.get(key);
if oldVal == null
{
   map2.put(key, newVal);
}
else
{
   map2.put(key, newVal + oldVal);
}

Obviously you have to loop over the first map so you can process all of it's entries but that's trivial.

ventsyv
  • 3,316
  • 3
  • 27
  • 49
  • ı know but ı cant write it someone ask similar questions but in scala ı dont know scala http://stackoverflow.com/questions/7076128/best-way-to-merge-two-maps-and-sum-the-values-of-same-key – Melike Ttkn Mar 04 '15 at 22:32