How can I not overwrite values via putAll() but instead add to the current value?

Viewed 400

Suppose I have two hash maps:

HashMap <String, Integer> h1;
h1.put("hi", 30);
h1.put("hi2",20);
h1.put("hi3",10);
h1.put("hi4",20);


HashMap <String, Integer> h2;
h2.put("hi", 20);
h2.put("hi2", 20);
h2.put("hi3", 20);
h2.put("hi4", 20);

My question is, if I do the following

h2.putAll(h1);

How could I update the values of h2 to be the sum, instead of just overwriting it? That is I want

[{"hi"=50}]
[{"hi2"=40}]
[{"hi3"=30}]
[{"hi4"=40}]

Instead of this

[{"hi"=30}]
[{"hi2"=20}]
[{"hi3"=10}]
[{"hi4"=20}]

Note: no functional constructs (including lambdas) and external libraries are allowed

3 Answers

You can use merge method for that:

    h1.forEach((key, value) -> h2.merge( key, value, Integer::sum));
    System.out.println(h2);

The old fashion way:

    for(String key : h1.keySet()){
        Integer v1 = h1.get(key);
        Integer v2 = h2.get(key);
        h2.put(key, (v2 == null) ? v1 : v1 + v2);
    }
    System.out.println(h2);

You can merge the two maps as shown below:

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

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> h1 = new HashMap<>();
        h1.put("hi", 30);

        Map<String, Integer> h2 = new HashMap<>();
        h2.put("hi", 20);
        
        Map<String, Integer> h3 = new HashMap<>(h1);
        h2.forEach(
            (key, value) -> h3.merge(key, value, (v1, v2) -> v1 + v2)
        );
        
        System.out.println(h3);
    }
}

Output:

{hi=50}

Non-Lambda solution:

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

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> h1 = new HashMap<>();
        h1.put("hi", 30);

        Map<String, Integer> h2 = new HashMap<>();
        h2.put("hi", 20);

        Map<String, Integer> h3 = new HashMap<>(h1);

        for (Entry<String, Integer> entry : h2.entrySet()) {
            String key = entry.getKey();
            h3.put(key, entry.getValue() + h1.getOrDefault(key, 0));
        }

        System.out.println(h3);
    }
}

Output:

{hi=50}

In traditional Java (before functional constructs), you would just iterate over the map and get the values from the other map

Map<String, Integer> h1 = new HashMap<>();
h1.put("hi", 30);

Map<String, Integer> h2 = new HashMap<>();
h2.put("hi", 20);

for (Map.Entry<String, Integer> entry : h2.entrySet()) {
    String key = entry.getKey();
    Integer toAdd = h1.get(key);
    if (toAdd != null) {
        entry.setValue(entry.getValue() + toAdd);
    }
}

System.out.println("h1 = " + h1);
System.out.println("h2 = " + h2);

Which prints

h1 = {hi=30}
h2 = {hi=50}

To go further, if the expected result should be that h2 should also contain every non-matching key from h1, then you can use the following

Map<String, Integer> h1 = new HashMap<>();
h1.put("hi", 30);
h1.put("hii", 40);

Map<String, Integer> h2 = new HashMap<>();
h2.put("hi", 20);

for (Map.Entry<String, Integer> entry : h1.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    Integer toPossiblyMerge = h2.get(key);
    if (toPossiblyMerge == null) {
        h2.put(key, value);
    } else {
        h2.put(key, value + toPossiblyMerge);
    }
}

System.out.println("h1 = " + h1);
System.out.println("h2 = " + h2);

Which prints

h1 = {hi=30, hii=40}
h2 = {hi=50, hii=40}
Related