在下面的代码中,我发现如果我们在参数中传递地图,使用 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后对象发生变化?
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")
因为在
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()
时,它实际上会更改第一个元素,并且更改将得到反映。来自 的文档:
此调用的效果相当于在指定映射中针对从键 k 到值 v 的每个映射在此映射上调用所以两者是等价的
put(k, v)
一次。
Map<Integer, Etudiant> map2= new HashMap<>();
map2.put(1,map.get(1));
map2.put(2,map.get(2));
你应该得到预期的结果
putAll
和一系列
put
都会达到相同的结果。但根据 Map 的实现,
putAll
有时会更快。例如,如果对映射的写入需要获取锁,那么
putAll
可以获取一次锁并将其用于所有锁。或者,如果映射必须在写入之间执行一些内部维护或核算,它也可以优化这些。如果您手头已经有一个集合,这也是一个很好的单行句,因此它比循环更简洁。