ArrayList, clone, and primitive boxed values: shallow copy acting like a deep one!
Joined: Dec 10, 2007
I'd like to be pointed to some doc that explains -- or to an expert that can explain -- why the following behavior involving clone() and the Integer class occurs. (I've looked several places and found Java documentation and forum posts that get close to addressing this issue, but don't quite get there.)
Suppose we have a constructed, empty, ArrayList<Integer> list1. Then we
If we print out the contents of list2 we get [10,11]. No surprise; clone() makes a shallow copy, and cells 0 and 1 of list1 and list2 are (supposedly) sharing the Integer objects that contain 10 and 11, respectively.
But now, change a value in list1:
When we print list1, we get [100, 11], as expected. When we print list2, we get [10, 11] -- quite the surprise: since (supposedly) list1 and list2 are pointing to the same object, we though we'd get [100, 11]. Instead, we are getting two different values for the two lists, getting what appears to be a deep copy from clone(), contrary to its (very clearly) documented behavior.
This behavior seems to occur with all wrapper types, but not non-wrapper types, so I suspect it is an effect of autoboxing. It appears that the ArrayList<Integer> values are unboxed before the cloning is done and reboxed before they are added to list 2, or that the primitive values are acting like they are stored directly in the ArrayList(!), rather than references to their containing objects being stored there. I have found no Java documentation that lays out what's going on.
Can y'all shed some light on this?
author and iconoclast
You've made this a little more (well, a lot more) complex than it needs to be. It has nothing to do with boxing/unboxing, wrappers, immutability, or anything else.
First of all, most importantly, it's only the original clone() in Object that is documented to do a shallow copy of immediate members only. Other classes can and do override Object.clone() to modify its behavior. The Collection classes, in general, override clone() and implement it such that internal arrays and things are copied, but the objects they contain are not.
So after your clone() call, you've got two distinct ArrayList objects, each one containing a separate Object array. The two Object arrays each contain references to the same two Integer objects. If we had a whiteboard to share, I'd draw two ovals for the two ArrayList objects; they's have arrows pointing to two rectangles representing the arrays; there would be two circles representing the Integers; and each rectangle would have two arrows coming out of it, each array pointing to one Integer. So each Integer would have two arrows pointing to it; the other objects would each only have one or zero.
Now, let's add a third circle to represent the Integer 100. When you make that set call, you erase one of the arrows from one of the rectangles pointing to the "10" circle; you draw a new array from that same rectangle to the "100" circle. Each rectangle still has two arrows coming out; the "11" has two arrows going in, the "10" has only one, and the "100" has one too.
So hopefully you see that the contents of the arrays can be changed independently, even though, right after the clone(), the contents of those arrays are shared.
Suppose you have 2 Lists: 1. List1 [FooObject1, FooObject2] 2. List2 [FooObject1, FooObject2] and List2 is clone of List1
and FooObject1.someThing = 10;
So when ever you change anything in FooObject1 on List1 e.g. List1.get(0).setSomeThing("100") then you do List2.get(0).getSomeThing() you will get 100.
As you just created the clone of the List but not clone of FooObject1 or FooObject2 which is contained in the List, so a shallow clone, when you even create clones of Objects contained in the Other Objects upto nth Level then you call it a deep clone.