aspose file tools*
The moose likes Java in General and the fly likes ArrayList, clone, and primitive boxed values: shallow copy acting like a deep one! Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Spring in Action this week in the Spring forum!
JavaRanch » Java Forums » Java » Java in General
Bookmark "ArrayList, clone, and primitive boxed values: shallow copy acting like a deep one!" Watch "ArrayList, clone, and primitive boxed values: shallow copy acting like a deep one!" New topic
Author

ArrayList, clone, and primitive boxed values: shallow copy acting like a deep one!

Norman Jacobson
Greenhorn

Joined: Dec 10, 2007
Posts: 1
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

list1.add(10);
list1.add(11);

Now, we make list2 a clone of list1:

ArrayList<Integer> list2 = (ArrayList<Integer> list1.clone();

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:

list1.set(0, 100);

When we print list1, we get [100, 11], as expected.
When we print list2, we get [10, 11] -- quite the surprise: since
(supposedly) list1[0] and list2[0] 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?

Many thanks!
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24187
    
  34

Hi Norman,

Welcome to JavaRanch!

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.


[Jess in Action][AskingGoodQuestions]
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19720
    
  20

What the Collection API means with shallow copy, is that you will get a new collection of the same type, but the references inside are just copied.

If one of the objects stored is a list of its own, and you modify this list through list1, then this will reflect in list2, because the exact same list is used by both lists.


SCJP 1.4 - SCJP 6 - SCWCD 5 - OCEEJBD 6
How To Ask Questions How To Answer Questions
Mani Iyer
Greenhorn

Joined: Feb 16, 2007
Posts: 5
So in simple words, the internal data structures of the collections are deep-copied but the contents of the internal data structures are reference-copied. Is that right?

mani
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19720
    
  20

Exactly.
Ravish Ahuja
Greenhorn

Joined: Nov 29, 2004
Posts: 13
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.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: ArrayList, clone, and primitive boxed values: shallow copy acting like a deep one!