循环中 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")
在下面的代码中,我发现如果我们在参数中传递地图,使用 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")