Green
Green

Reputation: 1465

HashMap Copy behavior I can't figure out

I am having trouble getting a separate copy of my HashMaps. By that I mean, once I have made a copy of the original, making a change to one does not change the other.

I have two HashMaps in this format:

HashMap<String, List<String> one = new HashMap<String, List<String>();
HashMap<String, List<String> two = new HashMap<String, List<String>();

I call the following function below (getTabSetDifferences) passing in one and two, as expected if there are some differences, those values will be removed from the HashMap and it'll be different than before it was passed in for the test.

I want them to remain unchanged, so tried passsing in:

getTabSetDifferences((HashMap)one.clone(), (HashMap)two.clone())

This still changed the originals, so i created two more hashmaps in the same format, and cloned one and two to them, I used the new hashmaps to pass in, and the original was still changed.

I then tried:

HashMap<String, List<String>> holdOne = new HashMap<String, List<String>>();
holdOne.putAll(one);

HashMap<String, List<String>> Holdtwo = new HashMap<String, List<String>>();
holdTwo.putAll(two);

Now I can do something like:

holdTwo.remove(key);

and the original is not changed, but if i call the method with holdOne and holdTwo it still changes the original one and two hashmaps, shouldn't they remain? The method is working, and finding the differences i want, and is returned. But I still need the original two hashmaps to be as they were, but no matter which way I call, what ever changes are made to holdOne and holdTwo changes the originals. Is that the expected behavior? If so, what is the proper way to get a copy of a hashmap that is not tied to it.

getTabSetDifferences(holdOne, holdTwo);

public HashMap<String, List<String>> getTabSetDifferences(HashMap<String, List<String>> hmMain, HashMap<String, List<String>> hmSecond)   {
    HashMap<String, List<String>> hmDifferences = new HashMap<String, List<String>>();
    for (Map.Entry<String, List<String>> entry : hmMain.entrySet()) {
        if(hmSecond.containsKey(entry.getKey())) {
            entry.getValue().removeAll(hmSecond.get(entry.getKey()));
            if (entry.getValue().size() > 0)
                hmDifferences.put(entry.getKey(), entry.getValue());
        }
        else {
            hmDifferences.put(entry.getKey(), entry.getValue());
        }
    }
    return hmDifferences;
}

Upvotes: 5

Views: 957

Answers (3)

jayunit100
jayunit100

Reputation: 17648

If you copy the list as a list (i.e. copy it at list scope, rather than some lower level implementation), then the pointer behavior will be seen.... However if you copy from one list into a new list, then those string objects are independant.

Java's clone method should not be use in expectation that it will return distinct , deep copies of an object - immutability is not a central concept to the way clone works.

I agree with the above comment : either use a multimap in a library like guava, or google collections, or simply be very careful about your copying, and only copy at the primitive levels, (don't ever copy a collection and expect it to be independent) unless you've tested this explicitly .

Upvotes: 2

Basanth Roy
Basanth Roy

Reputation: 6500

The clone method doesn't do a deep copy.

You have 2 options.

  1. create a deep copy method.
  2. Use one of the Map implementations from the java.util.concurrent package like copyOnWrite

Upvotes: 2

Peter Lawrey
Peter Lawrey

Reputation: 533530

I suspect you are only copying the keys/values. This will not create copies of the lists.

Perhaps Guava's MultiMap is what you want?

Upvotes: 2

Related Questions