Copying a HashMap in Java
Solution 1
If you want a copy of the HashMap you need to construct a new one with.
myobjectListB = new HashMap<Integer,myObject>(myobjectListA);
This will create a (shallow) copy of the map.
Solution 2
You can also use
clone()
Method to copy all elements from one hashmap to another hashmap
Program for copy all elements from one hashmap to another
import java.util.HashMap;
public class CloneHashMap {
public static void main(String a[]) {
HashMap hashMap = new HashMap();
HashMap hashMap1 = new HashMap();
hashMap.put(1, "One");
hashMap.put(2, "Two");
hashMap.put(3, "Three");
System.out.println("Original HashMap : " + hashMap);
hashMap1 = (HashMap) hashMap.clone();
System.out.println("Copied HashMap : " + hashMap1);
}
}
Solution 3
The difference is that in C++ your object is on the stack, whereas in Java, your object is in the heap. If A and B are Objects, any time in Java you do:
B = A
A and B point to the same object, so anything you do to A you do to B and vice versa.
Use new HashMap()
if you want two different objects.
And you can use Map.putAll(...)
to copy data between two Maps.
Solution 4
There is a small (HUGE) understatement here. If you want to copy a HashMap
with nested structures, HashMap.putAll()
will copy by reference, because it doesn't know how to exactly copy your object. For example:
import java.util.*;
class Playground {
public static void main(String[ ] args) {
Map<Integer, Map<Integer,List<Float>>> dataA = new HashMap<>();
Map<Integer, Map<Integer,List<Float>>> dataB = new HashMap<>();
dataA.put(1, new HashMap<>());
dataB.putAll(dataA);
assert(dataB.get(1).size() == 0);
dataA.get(1).put(2, new ArrayList<>());
if (dataB.get(1).size() == 1) { // true
System.out.println(
"Sorry object reference was copied - not the values");
}
}
}
So basically you will need to copy the fields yourself like here
List <Float> aX = new ArrayList<>(accelerometerReadingsX);
List <Float> aY = new ArrayList<>(accelerometerReadingsY);
List <Float> gX = new ArrayList<>(gyroscopeReadingsX);
List <Float> gY = new ArrayList<>(gyroscopeReadingsY);
Map<Integer, Map<Integer, Float>> readings = new HashMap<>();
Map<Integer,List<Float>> accelerometerReadings = new HashMap<>();
accelerometerReadings.put(X_axis, aX);
accelerometerReadings.put(Y_axis, aY);
readings.put(Sensor.TYPE_ACCELEROMETER, accelerometerReadings);
Map<Integer,List<Float>> gyroscopeReadings = new HashMap<>();
gyroscopeReadings.put(X_axis, gX);
gyroscopeReadings.put(Y_axis, gY);
readings.put(Sensor.TYPE_GYROSCOPE, gyroscopeReadings);
Solution 5
If we want to copy an object in Java, there are two possibilities that we need to consider: a shallow copy and a deep copy.
The shallow copy is the approach when we only copy field values. Therefore, the copy might be dependent on the original object. In the deep copy approach, we make sure that all the objects in the tree are deeply copied, so the copy is not dependent on any earlier existing object that might ever change.
This question is the perfect definition for the application of the deep copy approach.
First, if you have a simple HashMap<Integer, List<T>>
map then we just create a workaround like this. Creating a new instance of the List<T>
.
public static <T> HashMap<Integer, List<T>> deepCopyWorkAround(HashMap<Integer, List<T>> original)
{
HashMap<Integer, List<T>> copy = new HashMap<>();
for (Map.Entry<Integer, List<T>> entry : original.entrySet()) {
copy.put(entry.getKey(), new ArrayList<>(entry.getValue()));
}
return copy;
}
This one uses Stream.collect()
method to create the clone map, but uses the same idea as the previous method.
public static <T> Map<Integer, List<T>> deepCopyStreamWorkAround(Map<Integer, List<T>> original)
{
return original
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, valueMapper -> new ArrayList<>(valueMapper.getValue())));
}
But, if the instances inside T
are also mutable objects we have a big problem. In this case a real deep copy is an alternative that solves this problem. Its advantage is that at least each mutable object in the object graph is recursively copied. Since the copy is not dependent on any mutable object that was created earlier, it won’t get modified by accident like we saw with the shallow copy.
To solve that this deep copy implementations will do the work.
public class DeepClone
{
public static void main(String[] args)
{
Map<Long, Item> itemMap = Stream.of(
entry(0L, new Item(2558584)),
entry(1L, new Item(254243232)),
entry(2L, new Item(986786)),
entry(3L, new Item(672542)),
entry(4L, new Item(4846)),
entry(5L, new Item(76867467)),
entry(6L, new Item(986786)),
entry(7L, new Item(7969768)),
entry(8L, new Item(68868486)),
entry(9L, new Item(923)),
entry(10L, new Item(986786)),
entry(11L, new Item(549768)),
entry(12L, new Item(796168)),
entry(13L, new Item(868421)),
entry(14L, new Item(923)),
entry(15L, new Item(986786)),
entry(16L, new Item(549768)),
entry(17L, new Item(4846)),
entry(18L, new Item(4846)),
entry(19L, new Item(76867467)),
entry(20L, new Item(986786)),
entry(21L, new Item(7969768)),
entry(22L, new Item(923)),
entry(23L, new Item(4846)),
entry(24L, new Item(986786)),
entry(25L, new Item(549768))
).collect(entriesToMap());
Map<Long, Item> clone = DeepClone.deepClone(itemMap);
clone.remove(1L);
clone.remove(2L);
System.out.println(itemMap);
System.out.println(clone);
}
private DeepClone() {}
public static <T> T deepClone(final T input)
{
if (input == null) return null;
if (input instanceof Map<?, ?>) {
return (T) deepCloneMap((Map<?, ?>) input);
} else if (input instanceof Collection<?>) {
return (T) deepCloneCollection((Collection<?>) input);
} else if (input instanceof Object[]) {
return (T) deepCloneObjectArray((Object[]) input);
} else if (input.getClass().isArray()) {
return (T) clonePrimitiveArray((Object) input);
}
return input;
}
private static Object clonePrimitiveArray(final Object input)
{
final int length = Array.getLength(input);
final Object output = Array.newInstance(input.getClass().getComponentType(), length);
System.arraycopy(input, 0, output, 0, length);
return output;
}
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;
if (input instanceof LinkedList<?>) {
clone = new LinkedList<>();
} else if (input instanceof SortedSet<?>) {
clone = new TreeSet<>();
} else if (input instanceof Set) {
clone = new HashSet<>();
} else {
clone = new ArrayList<>();
}
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;
if (map instanceof LinkedHashMap<?, ?>) {
clone = new LinkedHashMap<>();
} else if (map instanceof TreeMap<?, ?>) {
clone = new TreeMap<>();
} else {
clone = new HashMap<>();
}
for (Map.Entry<K, V> entry : map.entrySet()) {
clone.put(deepClone(entry.getKey()), deepClone(entry.getValue()));
}
return clone;
}
}
user691305
Updated on September 17, 2020Comments
-
user691305 over 3 years
I am trying to keep a temporary container of a class that contains member :
HashMap<Integer,myObject> myobjectHashMap
A class called myobjectsList
Then I do
myobjectsListA = new myobjectsList(); myobjectsListB = new myobjectsList();
then: Add some hashmap items to A (like 2)
then:
myobjectListB = myobjectListA; //B has 2
then: Add hashmap items to A (like 4 more)
then: return A to the items stored in B
myobjectListA = myobjectListb;
But when I do this, B grows with A while I am adding hashmap items to A. A now has 6 items in it because B had 6.
I want A to still have the original 2 at the end after last assignment. In C++ I would use copy with objects, what is the java equivalent?
Added: OK I left something out explaining this.
MyObjectsList
does not contain the HashMap, it is derived from a classMyBaseOjbectsList
which has the HashMap member andMyObjectsList
extendsMyBaseOjbectsList
. Does this make a difference? -
Kevin Welker about 12 yearsor if it is already created, use
myObjectListB.addAll(myObjectListA)
-
user691305 about 12 yearsMaybe i'm not understanding this but I don't need a copy of the map. I need a copy of the class that holds the map. ? There for myObjectListB has to be a class derived from MyojbectsList not a hashmap.
-
user691305 about 12 yearsIs that the same as saying Java is all by reference and C++ is sometimes reference and sometimes value? But I don't want a HashMap, I wan't a duplicate copy of the class I can use as temp storage.
-
user691305 about 12 yearsClone did the same thing it just seemed to pass the reference.
-
user691305 about 12 yearsthat explains the problem, whats the solution? Or is this a limitation of Java with no work around?
-
user691305 about 12 yearsI don't have access to the HashMap in the base class. Again, I have to work with classes derived from the base. I cannot access the HashMap in it.
-
assylias about 12 yearsThat's not a limitation, it is the way it works. If you want 2 different objects, you create 2 objects - if you only create one object, then all the variables that point to that object are equal.
-
assylias about 12 yearsI suggest you create a new question with all the relevant code (ideally a SSCCE) and explain precisely what you are trying to achieve.
-
user691305 about 12 yearsAlso I don't understand the implementation of "addAll(myOjbejctListA)"
-
user691305 about 12 yearsAlso, when one increases its HashMap count, the copy does too, don't want that. I want idependent containers(in this case classes) of the same type.
-
user691305 about 12 yearscreating a new object and wiping out the original values is not the goal. The goal is to keep the original values and use only them again later in a copy that received more new values.
-
assylias about 12 yearsWell it really depends on the object. If it does not provide a way to create a new copy object, then it is complicated because the object has not been designed to be copied (maybe on purpose - but you can still do it using reflection). If the object does implement that feature (see for example the answer with the copy constructor of HashMap) then you just use the provided method.
-
Visionary Software Solutions almost 11 yearsYou can, but you probably shouldn't because Cloneable is essentially broken, per Joshua Bloch's Effective Java #11.
-
Visionary Software Solutions almost 11 yearscopy constructors or a copy static factories tend to be the way to go here.
-
Vinay W over 10 yearsalso, note that clone() returns a shallow copy.
-
Andrea Motto about 8 years@Kevin Welker,
addAll
is forHashSet
.putAll
is forHashMap
. -
Azurespot over 7 yearsWhat does shallow even mean? If it does not copy the map exactly (with keys/values intact), why bother? Might as well create a new map, right?
-
Thomio about 6 yearsIt didn't work. In my case the "new" variable were still pointing to the same object.
-
ratchet freak about 6 years@Thomio I mentioned that, that's what "shallow copy" means, If you need a deep copy to have to loop over the map and copy what you need.
-
nofunatall about 6 years@Azurespot shallow means it won't copy any nested objects and their nested objects...
-
Ariel almost 6 yearsIf you look at the source, all clone() does is create a new
HashMap
andputAll
everything into it. It's completely pointless.