将 HashMap 复制到另一个 HashMap
Copying HashMap to another HashMap
我在将 HashMap A 复制到 HashMap B 时遇到问题。B 始终与 A 相同。我的想法是仅使用 HashMap 制作一个小游戏。
Map<Point,Tile> A = new HashMap<Point,Tile>();
HashMap 有两个东西。一个点(键)和一个瓦片对象,这是我制作的另一个class。 Tile 接受两个整数和一个字符串。 (新瓷砖(x,y,字符串))。前两个整数定义点 x 和 y,字符串表示它是 "OFF" 还是 "ON".
我首先做的是用 2*2 个元素填充 HashMap A。
for(int i=0; i<2;i++){
for(int j=0; j<2;j++){
Tile t = new Tile(i, j, "OFF");
A.put(new Point(i,j), t);
}
}
然后我通过在构造函数中添加 A 将 HashMap A 复制到 HashMap B。我的想法是,我可以通过在构造函数中使用 HashMap B 返回到默认的 HashMap A(见下文)
Map<Point,Tile> B = new HashMap<Point,Tile>(A);
然后我将图块 (1,1) 更改为 "ON"
Tile t2 = A.get(new Point(1,1));
t2.setS("ON");
我的一块瓷砖现在 "ON"。现在我想将板重置回原来的状态(在填充阶段之后)。我清除 HashMap A 并使用 HashMap B 作为构造函数创建一个新的 HashMap。
A.clear();
A = new HashMap<Point,Tile>(B);
但是,当我将 HashMap A 上的 tile (1,1) 更改为 ON 时,它也会更新 HashMap B。我以为用构造函数创建一个新的 HashMap 会创建一个新的副本,但似乎不起作用。
奇怪的是
Map<Point,String> A = new HashMap<Point,String>();
可以,但不能
Map<Point,Tile> A = new HashMap<Point,Tile>();
我想以某种方式获取 HashMap A 的原始内容,而无需再次尝试遍历元素。
这是我的主要 class 代码
package main;
import java.awt.Point;
import java.util.HashMap;
import java.util.Map;
import model.Tile;
public class Test {
public static void main(String[] args) {
//list1
Map<Point,Tile> A = new HashMap<Point,Tile>();
//Populating map
for(int i=0; i<2;i++){
for(int j=0; j<2;j++){
Tile t = new Tile(i, j, "OFF");
A.put(new Point(i,j), t);
}
}
//copying list1 to list2
Map<Point,Tile> B = new HashMap<Point,Tile>(A);
//Change tile on 1,1 to ON
Tile t2 = A.get(new Point(1,1));
t2.setS("ON");
for(int i=0; i<2;i++){
for(int j=0; j<2;j++){
Tile tTemp = A.get(new Point(i,j));
System.out.println(i+" "+j+" "+tTemp.getS());
}
}
//Reseting tiles
//clear first list
A.clear();
System.out.println("");
//copy second list to first list
A = new HashMap<Point,Tile>(B);
for(int i=0; i<2;i++){
for(int j=0; j<2;j++){
Tile tTemp = A.get(new Point(i,j));
System.out.println(i+" "+j+" "+tTemp.getS());
}
}
}
}
这是瓷砖 class
package main;
public class Tile {
public int x,y;
public String s;
public Tile(int x1, int y1, String st){
x=x1;
y=y1;
s=st;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
}
这是在清除 HashMap A 之前打印的内容
0 0 OFF
0 1 OFF
1 0 OFF
1 1 ON
这是清除 HashMap A 并将 B 复制到其中后打印的内容。
0 0 OFF
0 1 OFF
1 0 OFF
1 1 ON
没有区别。
However, when I changed tile (1,1) to ON on HashMap A , it updated HashMap B as well.
当你写:
Tile t2 = A.get(new Point(1,1));
t2.setS("ON");
您没有更改任何一张地图。这些地图仅引用了 Point
个对象和 Tile
个对象。它们具有 相同的 引用 - 复制地图不会克隆其中的对象。
因此,当您更改其中一个对象的内容时,无论您使用哪个地图导航到该对象,您都会看到该更改。
换句话说,考虑这种情况:
- 我把地址写在两张纸上
- 我给查理一张纸,给乔一张纸
- 查理用这张纸找到了我的房子,并将我的前门漆成了红色
- 乔用这张纸找到我的房子并观察门的颜色
乔会看到一扇红色的前门,是吗?这里完全一样
如果您使 Point
和 Tile
类 不可变,这将不是问题 - 因为您无法更改现有对象的内容。在这一点上,复制引用和克隆对象之间没有特别有意义的区别。你最终会写成这样的东西:
Point p = new Point(1, 1);
Tile t2 = A.get(p);
t2 = t2.withS("ON"); // This would return a reference to a new object
A.put(t2);
Map<Point,Tile> B = new HashMap<Point,Tile>(A);
制作了 A
地图的浅表副本。它不会创建 Point
和 Tile
值的副本。它使用与原始地图中相同的引用。因此,当您更改 B
地图中的 Tile 时,相同的 Tile 也会在 A
地图中更改,反之亦然。
Map<Point,String> A = new HashMap<Point,String>();
有效
因为 String 是不可变的,所以与您的 Tile
实例不同,您无法更改 String
的状态。
要创建 A 的深层副本,您必须遍历 A 的条目,创建每个键和值的副本(假设 Point 键和 Tile 值都是可变的 - 如果 Point 不是可变的,创建 Tile 值的副本就足够了),并将副本放入 B 映射中。
Map<Point,Tile> B = new HashMap<Point,Tile>(A);
在需要深拷贝的时候进行浅拷贝。您必须手动实施深层复制或使用某些库为您完成。喜欢这个:
此处使用此实用程序class执行深度克隆。
//copying list1 to list2
Map<Point,Tile> B = DeepCopy.deepCopy(original)(A);
效用Class:
public final class DeepClone {
private DeepClone(){}
public static <X> X deepClone(final X input) {
if (input == null) {
return input;
} else if (input instanceof Map<?, ?>) {
return (X) deepCloneMap((Map<?, ?>) input);
} else if (input instanceof Collection<?>) {
return (X) deepCloneCollection((Collection<?>) input);
} else if (input instanceof Object[]) {
return (X) deepCloneObjectArray((Object[]) input);
} else if (input.getClass().isArray()) {
return (X) clonePrimitiveArray((Object) input);
}
return input;
}
private static Object clonePrimitiveArray(final Object input) {
final int length = Array.getLength(input);
final Object copy = Array.newInstance(input.getClass().getComponentType(), length);
// deep clone not necessary, primitives are immutable
System.arraycopy(input, 0, copy, 0, length);
return copy;
}
private static <E> E[] deepCloneObjectArray(final E[] input) {
final E[] clone = (E[]) Array.newInstance(input.getClass().getComponentType(), input.length);
for (int i = 0; i < input.length; i++) {
clone[i] = deepClone(input[i]);
}
return clone;
}
private static <E> Collection<E> deepCloneCollection(final Collection<E> input) {
Collection<E> clone;
// this is of course far from comprehensive. extend this as needed
if (input instanceof LinkedList<?>) {
clone = new LinkedList<E>();
} else if (input instanceof SortedSet<?>) {
clone = new TreeSet<E>();
} else if (input instanceof Set) {
clone = new HashSet<E>();
} else {
clone = new ArrayList<E>();
}
for (E item : input) {
clone.add(deepClone(item));
}
return clone;
}
private static <K, V> Map<K, V> deepCloneMap(final Map<K, V> map) {
Map<K, V> clone;
// this is of course far from comprehensive. extend this as needed
if (map instanceof LinkedHashMap<?, ?>) {
clone = new LinkedHashMap<K, V>();
} else if (map instanceof TreeMap<?, ?>) {
clone = new TreeMap<K, V>();
} else {
clone = new HashMap<K, V>();
}
for (Entry<K, V> entry : map.entrySet()) {
clone.put(deepClone(entry.getKey()), deepClone(entry.getValue()));
}
return clone;
}
}
参考:Assigning Hashmap to Hashmap
您需要通过克隆 Tile
对象来深度复制您的HashMap
。
默认情况下,HashMap
构造函数正在执行 浅拷贝 。它只是从传入的 Map
复制值,这适用于 primitives、String
s(和其他 immutable 对象)但不适用于对 Object
类型的引用,因为复制的引用将指向相同的原始对象,因此稍后会对其进行修改。
所以要解决这个问题,只需实现一个深度复制方法作为
public Map<Point, Tile> getDeepCopy(Map<Point, Tile> source) {
Map<Point, Tile> copy = new HashMap<Point, Tile>();
for (Map.Entry<Point, Tile> entry : source.entrySet())
copy.put(entry.getKey(), entry.getValue().clone());
return copy;
}
并使您的 Tile
class 实施 Cloneable
并 覆盖 clone()
方法作为
public class Tile implements Cloneable {
// other implementation
public Tile clone() throws CloneNotSupportedException {
return (Tile) super.clone();
}
}
您使用 Point
的方式,我认为也不需要 clone()
它们,但如果您也希望它们也被深度克隆,只需修改就像上面的Tile
。
我在将 HashMap A 复制到 HashMap B 时遇到问题。B 始终与 A 相同。我的想法是仅使用 HashMap 制作一个小游戏。
Map<Point,Tile> A = new HashMap<Point,Tile>();
HashMap 有两个东西。一个点(键)和一个瓦片对象,这是我制作的另一个class。 Tile 接受两个整数和一个字符串。 (新瓷砖(x,y,字符串))。前两个整数定义点 x 和 y,字符串表示它是 "OFF" 还是 "ON".
我首先做的是用 2*2 个元素填充 HashMap A。
for(int i=0; i<2;i++){
for(int j=0; j<2;j++){
Tile t = new Tile(i, j, "OFF");
A.put(new Point(i,j), t);
}
}
然后我通过在构造函数中添加 A 将 HashMap A 复制到 HashMap B。我的想法是,我可以通过在构造函数中使用 HashMap B 返回到默认的 HashMap A(见下文)
Map<Point,Tile> B = new HashMap<Point,Tile>(A);
然后我将图块 (1,1) 更改为 "ON"
Tile t2 = A.get(new Point(1,1));
t2.setS("ON");
我的一块瓷砖现在 "ON"。现在我想将板重置回原来的状态(在填充阶段之后)。我清除 HashMap A 并使用 HashMap B 作为构造函数创建一个新的 HashMap。
A.clear();
A = new HashMap<Point,Tile>(B);
但是,当我将 HashMap A 上的 tile (1,1) 更改为 ON 时,它也会更新 HashMap B。我以为用构造函数创建一个新的 HashMap 会创建一个新的副本,但似乎不起作用。
奇怪的是
Map<Point,String> A = new HashMap<Point,String>();
可以,但不能
Map<Point,Tile> A = new HashMap<Point,Tile>();
我想以某种方式获取 HashMap A 的原始内容,而无需再次尝试遍历元素。
这是我的主要 class 代码
package main;
import java.awt.Point;
import java.util.HashMap;
import java.util.Map;
import model.Tile;
public class Test {
public static void main(String[] args) {
//list1
Map<Point,Tile> A = new HashMap<Point,Tile>();
//Populating map
for(int i=0; i<2;i++){
for(int j=0; j<2;j++){
Tile t = new Tile(i, j, "OFF");
A.put(new Point(i,j), t);
}
}
//copying list1 to list2
Map<Point,Tile> B = new HashMap<Point,Tile>(A);
//Change tile on 1,1 to ON
Tile t2 = A.get(new Point(1,1));
t2.setS("ON");
for(int i=0; i<2;i++){
for(int j=0; j<2;j++){
Tile tTemp = A.get(new Point(i,j));
System.out.println(i+" "+j+" "+tTemp.getS());
}
}
//Reseting tiles
//clear first list
A.clear();
System.out.println("");
//copy second list to first list
A = new HashMap<Point,Tile>(B);
for(int i=0; i<2;i++){
for(int j=0; j<2;j++){
Tile tTemp = A.get(new Point(i,j));
System.out.println(i+" "+j+" "+tTemp.getS());
}
}
}
}
这是瓷砖 class
package main;
public class Tile {
public int x,y;
public String s;
public Tile(int x1, int y1, String st){
x=x1;
y=y1;
s=st;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
}
这是在清除 HashMap A 之前打印的内容
0 0 OFF
0 1 OFF
1 0 OFF
1 1 ON
这是清除 HashMap A 并将 B 复制到其中后打印的内容。
0 0 OFF
0 1 OFF
1 0 OFF
1 1 ON
没有区别。
However, when I changed tile (1,1) to ON on HashMap A , it updated HashMap B as well.
当你写:
Tile t2 = A.get(new Point(1,1));
t2.setS("ON");
您没有更改任何一张地图。这些地图仅引用了 Point
个对象和 Tile
个对象。它们具有 相同的 引用 - 复制地图不会克隆其中的对象。
因此,当您更改其中一个对象的内容时,无论您使用哪个地图导航到该对象,您都会看到该更改。
换句话说,考虑这种情况:
- 我把地址写在两张纸上
- 我给查理一张纸,给乔一张纸
- 查理用这张纸找到了我的房子,并将我的前门漆成了红色
- 乔用这张纸找到我的房子并观察门的颜色
乔会看到一扇红色的前门,是吗?这里完全一样
如果您使 Point
和 Tile
类 不可变,这将不是问题 - 因为您无法更改现有对象的内容。在这一点上,复制引用和克隆对象之间没有特别有意义的区别。你最终会写成这样的东西:
Point p = new Point(1, 1);
Tile t2 = A.get(p);
t2 = t2.withS("ON"); // This would return a reference to a new object
A.put(t2);
Map<Point,Tile> B = new HashMap<Point,Tile>(A);
制作了 A
地图的浅表副本。它不会创建 Point
和 Tile
值的副本。它使用与原始地图中相同的引用。因此,当您更改 B
地图中的 Tile 时,相同的 Tile 也会在 A
地图中更改,反之亦然。
Map<Point,String> A = new HashMap<Point,String>();
有效
因为 String 是不可变的,所以与您的 Tile
实例不同,您无法更改 String
的状态。
要创建 A 的深层副本,您必须遍历 A 的条目,创建每个键和值的副本(假设 Point 键和 Tile 值都是可变的 - 如果 Point 不是可变的,创建 Tile 值的副本就足够了),并将副本放入 B 映射中。
Map<Point,Tile> B = new HashMap<Point,Tile>(A);
在需要深拷贝的时候进行浅拷贝。您必须手动实施深层复制或使用某些库为您完成。喜欢这个:
此处使用此实用程序class执行深度克隆。
//copying list1 to list2
Map<Point,Tile> B = DeepCopy.deepCopy(original)(A);
效用Class:
public final class DeepClone {
private DeepClone(){}
public static <X> X deepClone(final X input) {
if (input == null) {
return input;
} else if (input instanceof Map<?, ?>) {
return (X) deepCloneMap((Map<?, ?>) input);
} else if (input instanceof Collection<?>) {
return (X) deepCloneCollection((Collection<?>) input);
} else if (input instanceof Object[]) {
return (X) deepCloneObjectArray((Object[]) input);
} else if (input.getClass().isArray()) {
return (X) clonePrimitiveArray((Object) input);
}
return input;
}
private static Object clonePrimitiveArray(final Object input) {
final int length = Array.getLength(input);
final Object copy = Array.newInstance(input.getClass().getComponentType(), length);
// deep clone not necessary, primitives are immutable
System.arraycopy(input, 0, copy, 0, length);
return copy;
}
private static <E> E[] deepCloneObjectArray(final E[] input) {
final E[] clone = (E[]) Array.newInstance(input.getClass().getComponentType(), input.length);
for (int i = 0; i < input.length; i++) {
clone[i] = deepClone(input[i]);
}
return clone;
}
private static <E> Collection<E> deepCloneCollection(final Collection<E> input) {
Collection<E> clone;
// this is of course far from comprehensive. extend this as needed
if (input instanceof LinkedList<?>) {
clone = new LinkedList<E>();
} else if (input instanceof SortedSet<?>) {
clone = new TreeSet<E>();
} else if (input instanceof Set) {
clone = new HashSet<E>();
} else {
clone = new ArrayList<E>();
}
for (E item : input) {
clone.add(deepClone(item));
}
return clone;
}
private static <K, V> Map<K, V> deepCloneMap(final Map<K, V> map) {
Map<K, V> clone;
// this is of course far from comprehensive. extend this as needed
if (map instanceof LinkedHashMap<?, ?>) {
clone = new LinkedHashMap<K, V>();
} else if (map instanceof TreeMap<?, ?>) {
clone = new TreeMap<K, V>();
} else {
clone = new HashMap<K, V>();
}
for (Entry<K, V> entry : map.entrySet()) {
clone.put(deepClone(entry.getKey()), deepClone(entry.getValue()));
}
return clone;
}
}
参考:Assigning Hashmap to Hashmap
您需要通过克隆 Tile
对象来深度复制您的HashMap
。
默认情况下,HashMap
构造函数正在执行 浅拷贝 。它只是从传入的 Map
复制值,这适用于 primitives、String
s(和其他 immutable 对象)但不适用于对 Object
类型的引用,因为复制的引用将指向相同的原始对象,因此稍后会对其进行修改。
所以要解决这个问题,只需实现一个深度复制方法作为
public Map<Point, Tile> getDeepCopy(Map<Point, Tile> source) {
Map<Point, Tile> copy = new HashMap<Point, Tile>();
for (Map.Entry<Point, Tile> entry : source.entrySet())
copy.put(entry.getKey(), entry.getValue().clone());
return copy;
}
并使您的 Tile
class 实施 Cloneable
并 覆盖 clone()
方法作为
public class Tile implements Cloneable {
// other implementation
public Tile clone() throws CloneNotSupportedException {
return (Tile) super.clone();
}
}
您使用 Point
的方式,我认为也不需要 clone()
它们,但如果您也希望它们也被深度克隆,只需修改就像上面的Tile
。