17

他の用途のためにコピーしたいハッシュマップがあります。ただし、コピーして再利用すると、元のファイルも変更されます。何故ですか?

    do {
            Map<Integer, Map<String, Object>> map1 = originalMap; 
            //at the second iteration originalMap is the same as map1 of the last iteration, 
            //eventhough the change was nog accepted;
            //do something with map1 (change value);
            if(change is accepted) {
               originalMap = map1;
            }
        } while(iteration < 10);

前もって感謝します

    public static <Integer,String, Schedule>Map<Integer, Map<String, Schedule>> deepCopy(Map<Integer, Map<String, Schedule>> original) {
    Map<Integer, Map<String, Schedule>> copy = new HashMap<Integer, Map<String, Schedule>>();

    for (Map.Entry<Integer, Map<String, Schedule>> entry : original.entrySet()) {
        copy.put(entry.getKey(), deepCopy2(entry.getValue()));
    }
    return copy;
}

public static <String, Schedule>Map<String, Schedule> deepCopy2(Map<String, Schedule> original) {
    Map<String, Schedule> copy = new HashMap<String, Schedule>();
    for (Map.Entry<String, Schedule> entry : original.entrySet()) {
        copy.put(entry.getKey(), entry.getValue());
    }

    return copy;
}
4

5 に答える 5

54

あなたがしたことは、地図のコピーを作成することではなく、それへの参照を作成することでした。2つの参照が同じオブジェクトを指している場合、一方への変更はもう一方に反映されます。

解決策1:これが単純なタイプから別のタイプへのマップである場合は、代わりにこれを行います。

Map<SomeType, OtherType> map1 = new HashMap<SomeType, OtherType>(original); 

これはコピーコンストラクタと呼ばれます。ほとんどすべての標準のコレクションとマップの実装には1つあり、通常、単純な構造を複製する最も簡単な方法です。これは、不変SomeTypeOtherTypeある限り正常に機能します(たとえば、他のタイプ、、、ただしコレクション、日付、マップ、配列などは機能しません)。IntegerNumberBooleanString

そうでない場合は、他の回答者やコメント投稿者が指摘しているように、マップ値もコピーする必要があります。

解決策2:安全であるはずの迅速で汚いバージョンは次のとおりです。

Map<Integer, Map<String, Object>> original=new HashMap<Integer, Map<String,Object>>();
Map<Integer, Map<String, Object>> copy = 
        new HashMap<Integer, Map<String, Object>>();
for(Entry<Integer, Map<String, Object>> entry : original.entrySet()){
    copy.put(entry.getKey(), new HashMap<String, Object>(entry.getValue()));
}

しかし実際には、ディープコピー方式を提供するというハンターのアイデアが好きです。だからここに解決策3があります:ジェネリックパラメーターを使用した私自身のバージョン:

public static <K1, K2, V> Map<K1, Map<K2, V>> deepCopy(
    Map<K1, Map<K2, V>> original){

    Map<K1, Map<K2, V>> copy = new HashMap<K1, Map<K2, V>>();
    for(Entry<K1, Map<K2, V>> entry : original.entrySet()){
        copy.put(entry.getKey(), new HashMap<K2, V>(entry.getValue()));
    }
    return copy;
}

あなたはそれをこのように呼ぶことができます:

Map<Integer, Map<String, Object>> original=new HashMap<Integer, Map<String,Object>>();
// do stuff here
Map<Integer, Map<String, Object>> copy = deepCopy(original);

アップデート

マップ、コレクション、配列(プリミティブなど)のディープクローンを実行するクラスを一緒にハックしました。使用法:

Something clone = DeepClone.deepClone(original);

ここにあります:

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;
    }
}
于 2012-07-02T15:18:41.057 に答える
7

これを行うことによって:

Map<Integer, Map<String, Object>> copy = originalMap;

...マップをコピーするのではなく、まったく同じマップを参照する新しい変数を作成するだけです。この変数を使用して行った変更は、元のマップに反映されます。これらは、の同じオブジェクトを指しています。メモリー。別のマップをパラメーターとして受け取るコンストラクターを使用して、元のマップをコピーすることをお勧めします。

Map<Integer, Map<String, Object>> copy;
copy = new HashMap<Integer, Map<String, Object>>(originalMap);

上記のコードは、元のマップの浅いコピーを作成します。つまり、一方のマップ内の要素のを変更すると、変更はもう一方のマップに反映されますが、どちらのマップとその他は影響を受けません。それでも不十分な場合は、マップをコピーするときに、マップ内の要素のディープコピーを実行する必要があります。

于 2012-07-02T15:20:18.487 に答える
2

単純で簡単な解決策は、マップ内の値をループして、それらをマップにコピーすることです。

Map<Integer, Map<String, Object>> map1;

//iterate over the map copying values into new map
for(Map.Entry entry : originalMap.entrySet())
{
   map1.put(entry.getKey(), new HashMap<String, Object>(entry.getValue()));
}

より良い解決策は、これをメソッドでラップすることです。

public static <K,J,V> Map<K, Map<J, V>> deepCopy(Map<K, Map<J, V>> original)
{
    Map<K, Map<J, V>> copy;

    //iterate over the map copying values into new map
    for(Map.Entry<K, Map<J, V>> entry : original.entrySet())
    {
       copy.put(entry.getKey(), new HashMap<J, V>(entry.getValue()));
    }

    return copy;
}
于 2012-07-02T15:25:35.840 に答える
0

あなたのコードでは、originalMapは単にへの参照map1です。現在、これらは両方とも同じキーと値を指しています。これはJavaであり、オブジェクト参照の「=」は単なる参照割り当てであることに注意してください(深いコピーや浅いコピーではありません)。

Javaコレクションは通常、cloneまたはを介した何らかの形式の浅いコピーをサポートしputAllます。map1マップの場合、とmap2がタイプであると仮定して、HashMap<KeyType,ValueType>あるマップを別のマップの浅いコピー(つまり、別個のHashMapオブジェクトであるが、キーと値が共有されている)にしたい場合は、次のようにします。

HashMap<KeyType,ValueType> map1();
HashMap<KeyType,ValueType> map2();

map2.put(x1,v1); // map2 = {{x1,v1}}

map1.put(x2,v2); // map1 = {{x2,v2}}

map1 = map2.clone(); // map1 = {{x1,v1}}, with x2 and v2 gone

map2.clear();
map2.put(x3,v3); // map2 = {{x3,v3}}
map2.put(x4,v4); // map2 = {{x3,v3},{x4,v4}}

map1.put(x4,v5); // map1 = {{x1,v1}, {x4,v5}}

// add all of map2 into map1, replacing any mappings with shared keys
map1.putAll(map2); // map1 = {{x1,v1},{x3,v3},{x4,v4}}, notice how v5 is gone

別れを告げるなら、JavaAPIを見る習慣をつける必要があります。それはあなたに大いに役立ちます。

http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html

于 2012-07-02T15:31:12.097 に答える
0

これは少し遅れる可能性がありますが、別の簡単な解決策は、マップを出力ストリームにシリアル化し、新しいマップオブジェクトに逆シリアル化することです。これは、シングルトンパターンを破る最も簡単な方法の1つでもあります。

于 2012-07-02T19:04:53.103 に答える