johnny
johnny

Reputation: 5

Merging Two HashMaps With Duplicate Keys and Update Value

I have two HashMaps whose keys are String and whose values are MyObject. My goal is to merge these two hashmaps, and then return a hashmap containing all the objects from these to maps. But if the keys (Strings) are identical, instances with the same key should be stored as the same MyObject. I have successfully managed to do this.

My problem is that i´d like to update a field inside the instance with identical keys.

class MyObject
String name;
int counter;

MyObject(String name) {
this.name = name;
this.counter = 1;
}

// getters and setters...

My goal is to update the int counter every time the keys are identical in the hashmap.

This code will successfully merge these two maps, put i cant seem to find out how to update the field int counter in MyObject every time MyObject have the same keys.

class Main
HashMap<String, MyObject> map1 = new HashMap<>();
HashMap<String, MyObject> map2 = new HashMap<>();

//some code that will put some instances of MyObject into map1 and map2
// assume that least three instances have the same keys

HashMap<String, MyObject> map3 = new HashMap<>(map1);

map2.forEach((key, value) -> map3.merge(key, value, (v1, v2) -> new MyObject(v1.getName())));

Suppose these two objects have the same key;

MyObject.getCounter() = 3 in map1

MyObject.getCounter() = 5 in map2

The new MyObject.getCounter() should be 8.

Upvotes: 0

Views: 2417

Answers (2)

WJS
WJS

Reputation: 40057

You can do it as follows:

  • stream both entry sets.
  • collect to a map
  • and add them during the merge method

Create some data

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

map1.put("A", new MyObject(2));
map1.put("B", new MyObject(4));
map1.put("C", new MyObject(10));
map2.put("C", new MyObject(10));
map2.put("B", new MyObject(8));

Now create the map

Map<String, MyObject> result = Stream
        .concat(map1.entrySet().stream(),
                map2.entrySet().stream())
        .collect(Collectors.toMap(Entry::getKey,
                Entry::getValue, (a, b) -> {
                    a.setCounter(a.getCounter() + b.getCounter());
                    return a;
                }, HashMap::new));

result.entrySet().forEach(System.out::println);

Prints

A=2
B=12
C=20

You could also use the following, prior to collection.

        Map<String, MyObject> result = Stream.of(map1,map2)
                .flatMap(map->map.entrySet().stream())
                .collect(...);

Class definition

    
 class MyObject {
    
    int value;
    public MyObject(int v) {
        this.value = v;
    }
    public int getCounter() {
        return value;
    }
    
    public void setCounter(int a) {
        value = a;
    }
    
    public String toString() {
        return value + "";
    }
}

Upvotes: 3

Thiyagu
Thiyagu

Reputation: 17920

You have to set the counter value of the new MyObject that you create.

If the class MyObject has a setter,

Map<String, MyObject> map3 = new HashMap<>(map1);

map2.forEach((key, value) -> map3.merge(key, value, (v1, v2) -> {
        MyObject myObject = new MyObject(v1.getName());
        myObject.setCounter(v1.getCounter() + v2.getCounter());
        return myObject;
}));

If you have an overloaded constructor accepting the name and the counter like,

MyObject(String name, int counter) {
    this.name = name;
    this.counter = counter;
}

then, you can do like,

map2.forEach((key, value) -> map3.merge(key, value, 
    (v1, v2) -> new MyObject(v1.getName(), v1.getCounter() + v2.getCounter())));

Upvotes: 1

Related Questions