Search code examples
javacollectionsimmutability

What's the difference between java.util.Map.putAll(Map<>) and java.util.Map.put(Integer, Object) in a loop


In the code below, I found that using the putAll method can cause problems if we pass the map in parameter

public class Main {

    public static void main(String...strings ) {
        Etudiant e1=new  Etudiant(5, "A");
        Etudiant e2=new  Etudiant(6, "B");

        Map<Integer, Etudiant> map= new HashMap<>();
        map.put(1, e1);
        map.put(2, e2);

        Map<Integer, Etudiant> map2= new HashMap<>();
        map2.put(1,map.get(1));
        map2.put(1,map.get(2));

        changeMe(map2);
        System.out.println(map.get(1));

        Map<Integer, Etudiant> map3= new HashMap<>();
        map3.putAll(map);
        changeMe(map3);
        System.out.println(map.get(1));
    }

    private static void changeMe(Map<Integer, Etudiant> etudiants) {
        etudiants.get(1).name="K";
    }
}
}

Here is the ouput result:

Etudiant [age=5, name=A]
Etudiant [age=5, name=K]

Could you explain the difference ?

Why after the use of putAll the object changes ?


Solution

  • Your code explained in detail

    Etudiant e1=new  Etudiant(5, "A");
    Etudiant e2=new  Etudiant(6, "B");
    
    Map<Integer, Etudiant> map= new HashMap<>();
    map.put(1, e1);
    map.put(2, e2);
    

    map now contains {1=Etudiant(5, "A"), 2=Etudiant(6, "B")}

    Map<Integer, Etudiant> map2= new HashMap<>();
    map2.put(1,map.get(1));
    map2.put(1,map.get(2));
    

    map2 now contains {1=Etudiant(6, "B")}

    changeMe(map2);
    System.out.println(map.get(1));
    

    Etudiant(6, "B") has been renamed Etudiant(6, "K"), so:
    map now contains {1=Etudiant(5, "A"), 2=Etudiant(6, "K")}
    map2 now contains {1=Etudiant(6, "K")}
    and it printed:

    Etudiant(5, "A")

    Map<Integer, Etudiant> map3= new HashMap<>();
    map3.putAll(map);
    

    map3 content is a copy of map content, so:
    map3 now contains {1=Etudiant(5, "A"), 2=Etudiant(6, "K")}

    changeMe(map3);
    System.out.println(map.get(1));
    

    Etudiant(5, "A") has been renamed Etudiant(5, "K"), so:
    map now contains {1=Etudiant(5, "K"), 2=Etudiant(6, "K")}
    map2 now contains {1=Etudiant(6, "K")}
    map3 now contains {1=Etudiant(5, "K"), 2=Etudiant(6, "K")}
    and it printed:

    Etudiant(5, "K")

    The code works exactly the way you coded it to do.


    All the above can easily be seen by adding a bunch of print statements, which is one way to debug your code.

    public class Test {
        public static void main(String[] args) {
            Etudiant e1=new  Etudiant(5, "A");
            Etudiant e2=new  Etudiant(6, "B");
    
            Map<Integer, Etudiant> map= new HashMap<>();
            map.put(1, e1);
            map.put(2, e2);
            System.out.println("map:  " + map);
    
            Map<Integer, Etudiant> map2= new HashMap<>();
            map2.put(1,map.get(1));
            map2.put(1,map.get(2));
            System.out.println("map2: " + map2);
    
            changeMe(map2);
            System.out.println("map:  " + map);
            System.out.println("map2: " + map2);
            System.out.println(map.get(1));
    
            Map<Integer, Etudiant> map3= new HashMap<>();
            map3.putAll(map);
            System.out.println("map3: " + map3);
    
            changeMe(map3);
            System.out.println("map:  " + map);
            System.out.println("map2: " + map2);
            System.out.println("map3: " + map3);
            System.out.println(map.get(1));
        }
        private static void changeMe(Map<Integer, Etudiant> etudiants) {
            System.out.print("Renamed " + etudiants.get(1));
            etudiants.get(1).name="K";
            System.out.println(" to " + etudiants.get(1));
        }
    }
    class Etudiant {
        int id;
        String name;
        Etudiant(int id, String name) {
            this.id = id;
            this.name = name;
        }
        @Override
        public String toString() {
            return "Etudiant(" + this.id + ", \"" + this.name + "\")";
        }
    }
    

    Output

    map:  {1=Etudiant(5, "A"), 2=Etudiant(6, "B")}
    map2: {1=Etudiant(6, "B")}
    Renamed Etudiant(6, "B") to Etudiant(6, "K")
    map:  {1=Etudiant(5, "A"), 2=Etudiant(6, "K")}
    map2: {1=Etudiant(6, "K")}
    Etudiant(5, "A")
    map3: {1=Etudiant(5, "A"), 2=Etudiant(6, "K")}
    Renamed Etudiant(5, "A") to Etudiant(5, "K")
    map:  {1=Etudiant(5, "K"), 2=Etudiant(6, "K")}
    map2: {1=Etudiant(6, "K")}
    map3: {1=Etudiant(5, "K"), 2=Etudiant(6, "K")}
    Etudiant(5, "K")