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 {
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);
Thanks in advance
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;
}
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 (eg 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;
}
}
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.
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;
}
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
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.