【问题标题】:Copying HashMap to another HashMap将 HashMap 复制到另一个 HashMap
【发布时间】:2015-03-19 07:56:46
【问题描述】:

我在将 HashMap A 复制到 HashMap B 时遇到问题。B 始终与 A 相同。我的想法是仅使用 HashMaps 制作一个小型拼图游戏。

Map<Point,Tile> A = new HashMap<Point,Tile>();

HashMap 有两件事。一个点(键)和一个瓦片对象,这是我制作的另一个类。 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");

我的一个图块现在是“开启”的。现在我想将板重置为原始状态(在人口阶段之后)。我清除 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 的原始内容,而无需再次尝试遍历元素。

这是我的主要课程代码

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());
            }
        }

    }

}

这是瓦片类

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

没有区别。

【问题讨论】:

标签: java dictionary hashmap copy


【解决方案1】:

您需要通过克隆Tile 对象深拷贝您的HashMap

默认情况下,HashMap 构造函数正在执行浅拷贝。它只是从传入的 Map 复制值,这适用于 primitivesStrings(和其他 不可变 对象),但不适用于对 @ 的引用987654327@ 类型作为复制的引用将指向相同的原始对象,因此稍后对其进行修改。

所以要解决这个问题,只需实现一个深拷贝方法

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 类实现Cloneable覆盖 clone() 方法为

public class Tile implements Cloneable {

    // other implementation

    public Tile clone() throws CloneNotSupportedException {
        return (Tile) super.clone();
    }
}

您使用Points 的方式,我认为不需要clone(),但如果您也希望它们被深度克隆,只需将其修改为上面的Tile .

【讨论】:

  • 这很有意义。设法使用深拷贝来做到这一点。谢谢。
  • @AndroidJava 很高兴为您提供帮助。请务必考虑accepting 的答案(左侧的绿色复选标记)。
【解决方案2】:

但是,当我在 HashMap A 上将 tile (1,1) 更改为 ON 时,它也更新了 HashMap B。

当你写作时:

Tile t2 = A.get(new Point(1,1));
t2.setS("ON");

您不会更改任何一张地图。这些地图只引用了Point 对象和Tile 对象。它们具有相同的 引用 - 复制地图不会克隆其中的对象。

因此,当您更改其中一个对象的内容时,无论您使用哪个地图导航到该对象,您都会看到该更改。

换句话说,考虑这种情况:

  • 我把我的地址写在两张纸上
  • 我给查理一张纸,给乔一张纸
  • 查理用这张纸找到了我的房子并将我的前门涂成红色
  • Joe 用这张纸找到我的房子并观察门的颜色

乔会看到一个红色的前门,是吗?这里完全一样。

如果您将PointTile 类设置为不可变类,这将不是问题——因为您将无法更改现有对象的内容。那时,复制引用和克隆对象之间没有特别有意义的区别。你最终会写出这样的东西:

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);

【讨论】:

    【解决方案3】:
    Map<Point,Tile> B = new HashMap<Point,Tile>(A); 
    

    在您需要深拷贝时进行浅拷贝。您必须手动实现深层复制或使用某些库为您执行此操作。喜欢这个:

    Java Deep-Cloning library

    【讨论】:

      【解决方案4】:

      Map&lt;Point,Tile&gt; B = new HashMap&lt;Point,Tile&gt;(A); 对您的 A 映射进行浅拷贝。它不会创建 PointTile 值的副本。它使用与原始地图中相同的引用。因此,当您更改 B 映射中的 Tile 时,A 映射中的相同 Tile 也会更改,反之亦然。

      Map&lt;Point,String&gt; A = new HashMap&lt;Point,String&gt;(); 有效 因为 String 是不可变的,所以与您的 Tile 实例不同,您无法更改 String 的状态。

      要创建 A 的深层副本,您必须遍历 A 的条目,创建每个键和值的副本(假设 Point 键和 Tile 值都是可变的 - 如果 Point 不可变,创建 Tile 值的副本就足够了),并将副本放入 B 地图中。

      【讨论】:

        【解决方案5】:

        这里使用这个实用程序类来执行深度克隆。

        //copying list1 to list2
            Map<Point,Tile> B = DeepCopy.deepCopy(original)(A);
        

        实用程序类:

        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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-12-29
          • 2019-09-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-22
          • 1970-01-01
          • 2017-07-06
          相关资源
          最近更新 更多