В чем разница между java.util.Map.putAll(Map<>) и java.util.Map.put(Integer, Object) в цикле
В приведенном ниже коде я обнаружил, что использование метода 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 объект меняется?
3 ответа
Ваш код подробно объяснен
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, "А")
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, "К")
Код работает точно так, как вы его запрограммировали.
Все вышеперечисленное можно легко увидеть, добавив несколько операторов печати, что является одним из способов отладки вашего кода.
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()
это фактически изменит первый элемент, и изменение будет отражено.
Из документов для Map::putAll
:
Эффект этого вызова эквивалентен эффекту вызова
put(k, v)
на этой карте один раз для каждого отображения от ключа k до значения v в указанной карте.
Таким образом, два эквивалентны
Измените свой код на:
Map<Integer, Etudiant> map2= new HashMap<>();
map2.put(1,map.get(1));
map2.put(2,map.get(2));
И вы должны получить ожидаемые результаты
И то и другое putAll
и серия put
достигнет того же результата. Но в зависимости от реализации карты, putAll
иногда может быть быстрее. Например, если записи на карту должны получить блокировку, то putAll
может получить замок один раз и использовать его для всех замков. Или, если карта должна выполнять какое-то внутреннее обслуживание или учет между записями, она также может оптимизировать их.
Это также хорошая строка, если у вас уже есть коллекция, так что она менее многословна, чем цикл.