循环中 java.util.Map.putAll(Map<>) 和 java.util.Map.put(Integer, Object) 有什么区别

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

在下面的代码中,我发现如果我们在参数中传递地图,使用 putAll 方法会导致问题

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

输出结果如下:

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

你能解释一下区别吗?

为什么使用putAll后对象发生变化?

putAll 和一系列 put 将获得相同的结果。但是根据 Map 的实现,putAll 有时会更快。比如对map的写操作需要获取锁,那么putAll可以获取一次锁,对所有的锁都使用。或者,如果映射必须在写入之间执行一些内部维护或统计,它也可以优化这些。

如果您手头已有一个集合,它也是一个不错的单行代码,因此它比循环更简洁。

因为在 map2:

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

您覆盖了第一个元素,所以地图是:

[1, e2]

那么当你调用changeMe()时,它正在改变e2不是e1,所以当你打印e1 ],它将保持不变。然后,当您调用 putAll() 时,它实际上会更改第一个元素,并且会反映更改。

来自 Map::putAll 的文档:

The effect of this call is equivalent to that of calling put(k, v) on this map once for each mapping from key k to value v in the specified map.

所以两者是等价的


将您的代码更改为:

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

你应该会得到预期的结果

你的代码解释的很详细

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 现在包含 {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 现在包含 {1=Etudiant(6, "B")}

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

Etudiant(6, "B") 重命名 Etudiant(6, "K"),因此:
map 现在包含 {1=Etudiant(5, "A"), 2=Etudiant(6, "K")}
map2 现在包含 {1=Etudiant(6, "K")}
并打印:

Etudiant(5, "A")

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

map3 内容是 map 内容的副本,因此:
map3 现在包含 {1=Etudiant(5, "A"), 2=Etudiant(6, "K")}

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

Etudiant(5, "A") 重命名 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")

代码的工作方式与您编写代码的方式完全相同。


以上所有内容都可以通过添加一堆打印语句轻松查看,这是调试您的代码的一种方法。

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 + "\")";
    }
}

输出

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")