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

问题描述 投票:0回答:3

在下面的代码中,我发现如果我们在参数中传递地图,使用 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后对象发生变化?

java collections immutability
3个回答
6
投票

您的代码详细解释了

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

它打印:

学生(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")}

它打印:

学生(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")

3
投票

因为在

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

的文档:

此调用的效果相当于在指定映射中针对从键 k 到值 v 的每个映射在此映射上调用

put(k, v)

 一次。

所以两者是等价的


将代码更改为:

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

你应该得到预期的结果


1
投票

putAll

和一系列
put
都会达到相同的结果。但根据 Map 的实现,
putAll
有时会更快。例如,如果对映射的写入需要获取锁,那么
putAll
可以获取一次锁并将其用于所有锁。或者,如果映射必须在写入之间执行一些内部维护或核算,它也可以优化这些。

如果您手头已经有一个集合,这也是一个很好的单行句,因此它比循环更简洁。

© www.soinside.com 2019 - 2024. All rights reserved.