18

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:
  • map1.place, (map1.mark1, map2.mark2)

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

0

5 Answers 5

29

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)
        )
    );
Sign up to request clarification or add additional context in comments.

7 Comments

Yes, this was the other way I was thinking of doing it, I the only reason I went against it, was if they go with a parallel stream, then it might not come out correctly as you cant guarantee the order, so then the final merge may not be correct
@AshFrench parallel stream doesn't make much sense for such a simple task, you would pay the price of using several threads and synchronizing them even with big maps, I don't believe that it would be faster
@AshFrench the new approach will work even with parallel streams even if it doesn't make much sense
@Ash French: which ordering issue are you thinking of, that could arise with this approach, but not with your solution?
No, this will totally work with parallel streams; they don't lose their ordering.
|
19

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

Comments

3

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);
    }
}

Comments

3

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;
}

1 Comment

have you seen in java 8 the map.merge() function, it basically allows you to do the if(contains){get() put()} into one call
0

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

    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.