簡體   English   中英

將HashMap復制到另一個HashMap

[英]Copying HashMap to another HashMap

我在將HashMap A復制到HashMap B時遇到問題。B始終與A相同。我的想法是僅使用HashMaps制作小型圖塊游戲。

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

HashMap有2件事。 一個point(key)和一個tile對象,這是我制作的另一個類。 Tile接受兩個整數和一個字符串。 (新的Tile(x,y,string))。 前兩個整數定義點x和y,字符串告訴它的“ OFF”還是“ ON”。

我首先要做的是用2 * 2元素填充HashMapA。

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復制到HashMapB。 我的想法是,我可以通過在構造函數中使用HashMap B返回默認的HashMapA。

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時,它也更新了HashMapB。 我以為使用構造函數創建新的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

沒有不同。

您需要通過克隆 Tile對象來深度復制 HashMap

默認情況下, HashMap構造函數正在執行淺表復制 它只是從傳入的Map復制值,該值適用於Map String (和其他不可變對象),但不適用於對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類實現Cloneable重寫 clone()方法為

public class Tile implements Cloneable {

    // other implementation

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

在您使用Point的方式上,我也沒有看到也需要clone() ,但是如果您也想對其進行深克隆,只需將其修改為上方的Tile

但是,當我在HashMap A上將tile(1,1)更改為ON時,它也更新了HashMapB。

當你寫:

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

您沒有更改任何一張地圖。 這些地圖僅引用Point對象和Tile對象。 它們具有相同的引用-復制地圖不會在其中克隆對象。

因此,當您更改其中一個對象的內容時,無論使用哪種地圖導航到該對象,都將看到該更改。

換句話說,請考慮這種情況:

  • 我的地址寫在兩張紙上
  • 我給查理一張紙,給喬
  • 查理用紙找到我的房子,並將我的前門漆成紅色
  • 喬用紙來找到我的房子並觀察門的顏色

喬會看到紅色的前門,是嗎? 這里完全一樣。

如果將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);

Map<Point,Tile> B = new HashMap<Point,Tile>(A); 制作您的A地圖的淺表副本。 它不會創建PointTile值的副本。 它使用與原始地圖中相同的參考。 因此,當你在改變一個瓷磚B圖中,相同的瓷磚在改變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); 

在需要深拷貝時進行淺拷貝。 您必須手動實現深層復制,或者使用某些庫來為您完成。 像這個:

Java深度克隆庫

在這里使用此實用程序類執行深度克隆。

//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;
    }
}

參考: 將Hashmap分配給Hashmap

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM