dardy
dardy

Reputation: 433

Merge two maps with Java 8

I have two maps like this:

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

MyObject {
   Integer mark1;
   Integer mark2;
}

What I want to do to is to merge the two maps into a map3 <String, MyObject> like this:

  1. If map1.place is not in map2.place, then I add the entry to map3.
  2. same if map2.place is not in map1.place, I add the entry to map3.
  3. if map1.place is in map2.place, then I add this entry:

I have read about flatMap, but I really have a hard time using it. Any clue how to do this?

Upvotes: 18

Views: 37611

Answers (5)

Soudipta Dutta
Soudipta Dutta

Reputation: 2122

Case 1: Given a List of Maps. Then Join the maps according to the Key

    public class Test14 {

        public static void main(String[] args) {

            Map<String, List<Integer>> m1 = new HashMap<>();
            Map<String, List<Integer>> m2 = new HashMap<>();
            m1.put("a", List.of(1));
            m1.put("b", List.of(2, 3));
            m2.put("a", List.of(12, 115));
            m2.put("b", List.of(2, 5));
            m2.put("c", List.of(6));

            System.out.println("map1 => " + m1);
            System.out.println("map2 => " + m2);

            ArrayList<Map<String, List<Integer>>> maplist = new ArrayList<Map<String, List<Integer>>>();
            maplist.add(m1);
            //   map1 => {a=[1], b=[2, 3]}
            maplist.add(m2);
            //      map2 => {a=[12, 115], b=[2, 5], c=[6]}

            System.out.println("maplist => " + maplist);
         //    maplist => [{a=[1], b=[2, 3]}, {a=[12, 115], b=[2, 5], c=[6]}]


            //  flatmap does omitted {} 
            List<Entry<String, List<Integer>>> collect11 = 
                    maplist
                    .stream()
                    .flatMap(map -> map.entrySet().stream())
                    .collect(Collectors.toList());
            System.out.println(" collect11  => " + collect11);
            // collect11  => [a=[1], b=[2, 3], a=[12, 115], b=[2, 5], c=[6]]

            // That's why we will use this flatmap


            Map<String, List<Integer>> map2 = maplist.stream()
            .flatMap(map -> map.entrySet().stream())

            .collect(
                    Collectors.toMap(
                            //keyMapper 
                            Map.Entry::getKey,


                            //valueMapper
                            Map.Entry::getValue,

                            (list_a,list_b) -> Stream.concat(list_a.stream(), list_b.stream())
                            .collect(Collectors.toList())


                            )//tomap

                    );
//{a=[1, 12, 115], b=[2, 3, 2, 5], c=[6]}

            System.out.println("After joining the maps according the key  => " + map2);
            // After joining the maps according the key  => {a=[1, 12, 115], b=[2, 3, 2, 5], c=[6]}

            /*
             OUTPUT :

             After joining the maps according the key  => {a=[1, 12, 115], b=[2, 3, 2, 5], c=[6]}
             */

        }// main

    }

Upvotes: 0

Jerin Joseph
Jerin Joseph

Reputation: 1097

Incase of a simple merge you could use map3.putAll() as explained in How can I combine two HashMap objects containing the same types?

In your case, you would probably have to write some custom logic,

First populate map3 with map1. Then Iterate the map3 to find any duplicates with map2 in which case you replace the entry with the map1.place, (map1.mark1, map2.mark2) logic.

MapMerge

public class MapMerge {

    public static void main(String []args){


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

        map3.putAll(map1);

        for(Entry<String, MyObject> entry:map2.entrySet()){         
            if (map3.containsKey(entry.getKey())){                  
                MyObject map3Obj = map3.get(entry.getKey());
                map3.put(
                    entry.getKey(), 
                    new MyObject(map3Obj.getMark1(),entry.getValue().getMark2())
                );
            }
        }
    }
}

MyObject

class MyObject{

    public MyObject(Integer m1, Integer m2){
        mark1 = m1;
        mark2 = m2;
    }

    public Integer getMark1() {
        return mark1;
    }
    public void setMark1(Integer mark1) {
        this.mark1 = mark1;
    }
    public Integer getMark2() {
        return mark2;
    }
    public void setMark2(Integer mark2) {
        this.mark2 = mark2;
    }
    Integer mark1;
    Integer mark2;
}

Upvotes: 3

talex
talex

Reputation: 20436

Something like this should work.

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

Set<String> allKeys = new HashSet<String>();
allKeys.addAll(map1.keySet());
allKeys.addAll(map2.keySet());
for(String key : allKeys){
    MyObject v1 = map1.get(key);
    MyObject v2 = map2.get(key);
    if(v1 != null && v2 == null){
        result.put(key, v1);
    }else if(v1 == null && v2 !=null){
        result.put(key, v2);
    } else {
        MyObject newObject = new MyObject(v1.mark1, v2.mark2); 
        result.put(key, newObject);
    }
}

Upvotes: 3

Ash
Ash

Reputation: 2602

Here is what I think would work

Map<String, MyObj> map3 = new HashMap<>(map1);
map2.forEach(
    (key, value) -> map3.merge(key, value, (v1, v2) -> new MyObject(v1.mark1,v2.mark2))
);

The merge function is what is taking care of your scenario 3, in that if the key already exists, it creates a new MyObject with v1.mark1 and v2.mark2

Upvotes: 19

Nicolas Filotto
Nicolas Filotto

Reputation: 44965

It can be done using the Stream API with the appropriate mergeFunction as next:

Map<String, MyObject> map3 = Stream.of(map1, map2)
    .flatMap(map -> map.entrySet().stream())
    .collect(
        Collectors.toMap(
            Map.Entry::getKey,
            Map.Entry::getValue,
            (v1, v2) -> new MyObject(v1.getMark1(), v2.getMark2())
        )
    );

This concatenates entries of map1 followed by the entries of map2, then convert everything as a Map with a merge function that will use mark1 from the first value (the one from map1) and mark2 from the second value (the one from map2) in case of duplicate keys.


Or it could also be done using a different Supplier<Map> that will propose a map that already contains the entries of map1 then we can focus only on adding the entries of map2 as next:

Map<String, MyObject> map3 = map2.entrySet()
    .stream()
    .collect(
        Collectors.toMap(
            Map.Entry::getKey,
            Map.Entry::getValue,
            (v1, v2) -> new MyObject(v1.getMark1(), v2.getMark2()),
            () -> new HashMap<>(map1)
        )
    );

Upvotes: 29

Related Questions