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

What you did was not to create a copy of the map, but of the reference to it. when two references point to the same object, changes to one will reflect in the other.

Solution 1: If this was a Map from some simple type to another, you would do this instead:

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

This is called a Copy Constructor. Almost All standard Collection and Map implementations have one, and it's usually the simplest way to clone a simple structure. This will work fine as long as SomeType and OtherType are immutable (e.g. Integer and other Number types, Boolean, String, but not Collections, Dates, Maps, Arrays etc.)

If not, as other answerers and commenters have pointed out, you also need to copy the map values.

Solution 2: Here's a quick and dirty version that should be safe:

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

But actually, I like Hunter's idea of providing a deep copy method. So here's Solution 3: my own version using generic parameters:

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

You can call it like this:

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

Update

I've hacked together a class that performs deep cloning for Maps, Collections and Arrays (primitive and otherwise). Usage:

Something clone = DeepClone.deepClone(original);

Here it is:

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

By doing this:

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

... you're not copying the map, only creating a new variable which refers to the exact same map, and clearly the changes you make using this variable will be reflected in the original map - they're pointing to the same object in memory. Better copy the original map using the constructor that receives another map as a parameter:

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

The above code will create a shallow copy of the original map, meaning: if you change the value of the elements inside one map, the changes will be reflected in the other, but you can add/remove freely the elements from either map and the other won't be affected. If that's not good enough, you'll need to perform a deep copy of the elements in the map at the time of copying it.

于 2012-07-02T15:20:18.487 回答
2

一个简单直接的解决方案是遍历 Map 中的值并将它们复制到 Map 中:

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

这可能来得有点晚,但另一个简单的解决方案是将地图序列化为输出流并将其反序列化为新的地图对象。这也是打破单例模式的最简单方法之一。

于 2012-07-02T19:04:53.103 回答
0

在您的代码中,originalMap只是对map1. 现在,它们都指向相同的键和值。请记住,这是 Java,其中对象引用上的“=”只是一个引用分配(不是深拷贝或浅拷贝)。

cloneJava 集合通常通过或支持某种形式的浅拷贝putAll。在映射的情况下,假设map1map2的类型为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

临别之际,您需要养成查看 Java API 的习惯。这会对你有很大帮助。

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

于 2012-07-02T15:31:12.097 回答