Copying a HashMap in Java

243,806

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

source : http://www.tutorialdata.com/examples/java/collection-framework/hashmap/copy-all-elements-from-one-hashmap-to-another

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;
    }
}
Share:
243,806
user691305
Author by

user691305

Updated on September 17, 2020

Comments

  • user691305
    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 class MyBaseOjbectsList which has the HashMap member and MyObjectsList extends MyBaseOjbectsList. Does this make a difference?

  • Kevin Welker
    Kevin Welker about 12 years
    or if it is already created, use myObjectListB.addAll(myObjectListA)
  • user691305
    user691305 about 12 years
    Maybe 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
    user691305 about 12 years
    Is 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
    user691305 about 12 years
    Clone did the same thing it just seemed to pass the reference.
  • user691305
    user691305 about 12 years
    that explains the problem, whats the solution? Or is this a limitation of Java with no work around?
  • user691305
    user691305 about 12 years
    I 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
    assylias about 12 years
    That'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
    assylias about 12 years
    I suggest you create a new question with all the relevant code (ideally a SSCCE) and explain precisely what you are trying to achieve.
  • user691305
    user691305 about 12 years
    Also I don't understand the implementation of "addAll(myOjbejctListA)"
  • user691305
    user691305 about 12 years
    Also, 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
    user691305 about 12 years
    creating 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
    assylias about 12 years
    Well 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
    Visionary Software Solutions almost 11 years
    You can, but you probably shouldn't because Cloneable is essentially broken, per Joshua Bloch's Effective Java #11.
  • Visionary Software Solutions
    Visionary Software Solutions almost 11 years
    copy constructors or a copy static factories tend to be the way to go here.
  • Vinay W
    Vinay W over 10 years
    also, note that clone() returns a shallow copy.
  • Andrea Motto
    Andrea Motto about 8 years
    @Kevin Welker, addAll is for HashSet. putAll is for HashMap.
  • Azurespot
    Azurespot over 7 years
    What 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
    Thomio about 6 years
    It didn't work. In my case the "new" variable were still pointing to the same object.
  • ratchet freak
    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
    nofunatall about 6 years
    @Azurespot shallow means it won't copy any nested objects and their nested objects...
  • Ariel
    Ariel almost 6 years
    If you look at the source, all clone() does is create a new HashMap and putAll everything into it. It's completely pointless.