Home > database >  HashMap.clone() returns shallow copy, but doesn't reflect the Value Change
HashMap.clone() returns shallow copy, but doesn't reflect the Value Change

Time:06-18

Java doc says HashMap.clone() returns a shallow copy.

So I expect that if I change the value of some key in original HashMap, the cloned one will also see the change. Thus, I have this:

public class ShallowCopy {
    public static void main(String[] args) {
        HashMap<Integer, String> map = new HashMap<Integer, String>();
        map.put(2,"microsoft");
        map.put(3,"yahoo");
        map.put(1,"amazon");

        // Type safety: Unchecked cast from Object to
        // HashMap<Integer, String> Java(16777761)
        Map<Integer, String> mClone = 
            (HashMap<Integer, String>)map.clone();
        String previous = map.replace(3, "google");
        System.out.println(previous); // yahoo
        System.out.println(map.get(3)); // google
        System.out.println(mClone.get(3)); // yahoo, but why?
    }
}

In my code, I called HashMap.replace() and I see the value in map is changed from "yahoo" to "google". Strangely, the last line prints the previous value when looking for it in mClone.

But the fact is it prints "yahoo" not "google" as I expected. Where does it get wrong, please kindly fix my understandings?

Plus: I also got a compiler warning as I commented in my code(Java(16777761)), how to fix it?

CodePudding user response:

Method clone() creates a new map which gets populated with references to the values and keys contained in the source map, it's not a view of the initial map but an independent collection.

When you're calling map.replace(3, "google") a value mapped to the key 3 gets replaced with a new string, however the cloned map remains unaffected, it stills holds a reference to the same string "yahoo".

CodePudding user response:

TL;DR

After the cloning operation the values are simply references to the same object. So if you were to modify one reference in one map, the other would also be modified. But you didn't modify the object you replaced it. At that point it became distinct from a reference perspective.

Example

The clone operation is working just as you presumed. But you are interpreting the results incorrectly. Consider the following class.

class FooClass {
    int a;
    public FooClass(int a) {
        this.a = a;
    }
    public void setA(int a) {
        this.a = a;
    }
    @Override
    public String toString() {
        return a   "";
    }
}

And now create a map and its clone.

HashMap<Integer, FooClass> map = new HashMap<>();
map.put(10, new FooClass(25));
HashMap<Integer,FooClass> mClone = (HashMap<Integer,FooClass>)map.clone();

The values of each key are the same object reference. As shown by the following:

System.out.println(System.identityHashCode(map.get(10)));
System.out.println(System.identityHashCode(mClone.get(10)));

prints

1523554304
1523554304

So if I modify one, it will modify the other.

The same was true for the String values of your maps. But when you replaced "yahoo" with "google" you didn't modify the String you replaced it with a different Object.

If I were to do the same for FooClass, here is the result.

System.out.println("Modifying same object");
mClone.get(10).setA(99);
System.out.println(map.get(10));
System.out.println(mClone.get(10));

prints

Modifying same object
99
99

But if I were to replace the object with a new one.

System.out.println("Replacing the instance");
FooClass previous = mClone.replace(10, new FooClass(1000));
System.out.println("Previous = "   previous);
System.out.println("map: "   map.get(10));
System.out.println("mClone: "   mClone.get(10));

prints

Replacing the instance
Previous = 99
map: 99
mClone: 1000

And this latter operation is what you did.

  • Related