Assigning Hashmap to Hashmap

后端 未结 5 1282
轻奢々
轻奢々 2020-12-14 10:13

I have a hashmap which I want to copy for other use. But whenever I copy it and reuse it, it also changes the original one. Why is that?

    do {
                    


        
相关标签:
5条回答
  • 2020-12-14 10:25

    A simple and straightforward solution would be to just loop over the values in the Map and copy them into a 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()));
    }
    

    A better solution would be to wrap this in a method:

    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;
    }
    
    0 讨论(0)
  • 2020-12-14 10:27

    This might be coming a bit late, but another simple solution will be to Serialize the map to an output Stream and de-serialize it to a new Map Object. Thats also one of the easiest ways to break the singleton pattern.

    0 讨论(0)
  • 2020-12-14 10:41

    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;
        }
    }
    
    0 讨论(0)
  • 2020-12-14 10:43

    In your code, originalMap is simply a reference to map1. Now, they both point to the same keys and values. Remember, this is Java where '=' on object references is simply an reference assignment (not a deep or shallow copy).

    Java collections typically support some form of shallow copying via clone or putAll. In the case of maps, assuming map1 and map2 are of type HashMap<KeyType,ValueType>, if you want one map to be a shallow copy of another (meaning, a distinct HashMap object but with shared keys and values), you do this:

    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
    

    On a parting thought, you need to make it a habit of looking at the Java API. It will help you a lot.

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

    0 讨论(0)
  • 2020-12-14 10:47

    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.

    0 讨论(0)
提交回复
热议问题