Clone java collection & static variable??

M

mingclee1

Hello,

I have a question with clone() in java collection. Check the following
code:

import java.util.*;

/**
* @author mlee45
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class TestClone implements Cloneable {
private Entry[] inner_a1 = {new Entry(1), new Entry(2)};
private Entry[] inner_a2 = {new Entry(3), new Entry(4)};
public Object[] a1 = {new Entry(5), new Entry(6)};
public Object[] a2 = {inner_a1, inner_a2};

public ArrayList v1, v2 = new ArrayList();

public TestClone(){
initialize();
}

private void initialize(){
v1 = new ArrayList();
v1.add(0, new Entry(5));
v1.add(1, new Entry(6));
v2.add(0, inner_a1);
v2.add(1, inner_a2);
}

public Object clone() throws CloneNotSupportedException{
TestClone result = (TestClone)super.clone();
result.v1 = (ArrayList)v1.clone();
result.v2 = (ArrayList)v2.clone();
result.a1 = (Object[])a1.clone();
result.a2 = (Object[])a2.clone();
return result;
}

public static void main(String[] args){
TestClone tc = new TestClone();
try{
TestClone tc_2 = (TestClone)tc.clone();
tc_2.v1.set(0, new Entry(10));

//**************** test arrayList *******************
System.out.println("tc.v1[0] is"+tc.v1.get(0));
System.out.println("tc_2.v1[0] is"+tc_2.v1.get(0));
Entry[] inner_a3 = { new Entry(10), new Entry(11)};
tc_2.v2.set(0, inner_a3);
Entry[] e1 = (Entry[])tc.v2.get(0);
Entry[] e2 = (Entry[])tc_2.v2.get(0);

System.out.println("e1[0] is "+e1[0]);
System.out.println("e2[0] is "+e2[0]);

//******************** test array ********************
tc.a1[0] = new Entry(100);
System.out.println("tc.a1[0] is "+tc.a1[0]);
System.out.println("tc_2.a1[0] is "+tc_2.a1[0]);

Entry[] inner_a4 = {new Entry(1000), new Entry(1001)};
tc_2.a2[0] = inner_a4;

e1 = (Entry[])tc.a2[0];
e2 = (Entry[])tc_2.a2[0];
System.out.println("tc.a2[0] is "+e1[0]);
System.out.println("tc_2.a2[0] is "+e2[0]);

}catch(CloneNotSupportedException ce){
ce.printStackTrace();
}
}
}


class Entry{
int val;
public Entry(int _val){
val = _val;
}
public String toString(){
return Integer.toString(val);
}
}

The output is
tc.v1[0] is5
tc_2.v1[0] is10
e1[0] is 1
e2[0] is 10
tc.a1[0] is 100
tc_2.a1[0] is 5
tc.a2[0] is 1
tc_2.a2[0] is 1000


Question: It seems that clone() method called on array does a deep
copy instead of the default shallow copy. Is this an internal special
condition? Also, I tried to use both Vector and ArrayList, I got the
same result from both. I checked the API, it says clone() method for
vector is deep copy which explains my result, but clone() method for
ArrayList returns a shallow copy which doesn't explain my result? It
seems that any out of the box collection with out of the box clone()
method does deep copy?

Also, it was interesting to know that if I make the array or vector
defined above static, the call to clone() would still work, but result
would show only one object is created. Shouldn't it throw
CloneNotSupportedException like in final?

Thanks for your advice!
 
T

Thomas Hawtin

public Object clone() throws CloneNotSupportedException{
TestClone result = (TestClone)super.clone();

You are better off catching that exception here (and rethrowing it as an
error).
Question: It seems that clone() method called on array does a deep
copy instead of the default shallow copy. Is this an internal special

Not it doesn't. Your code is consistent with shallow copies. It is
however very difficult to follow.
condition? Also, I tried to use both Vector and ArrayList, I got the
same result from both. I checked the API, it says clone() method for
vector is deep copy which explains my result, but clone() method for

No it doesn't.

Tom Hawtin
 
M

mingclee1

Hello Tom,

Thanks for your repsonse, and sorry for the messy code.

Not it doesn't. Your code is consistent with shallow copies. It is
however very difficult to follow.

I thought a shallow copy would be when you call clone() on an array, it
creates a new object reference to the array, but the reference inside
the array stay the same? The output in the program shows otherwise.
It also clone object references inside the array. Isn't that the
behaviour of deep copy?

So, if a shallow copy on a java collection will clone the object
reference to the collection and clone the object reference it contains,
then a deep copy of java collection would be the above plus clone any
object references inside the object in the array. Ex:
class Entry{
int val
EntryList e_list // deep copy will also clone this?

Thanks
 
T

Thomas Hawtin

I thought a shallow copy would be when you call clone() on an array, it
creates a new object reference to the array, but the reference inside
the array stay the same? The output in the program shows otherwise.
It also clone object references inside the array. Isn't that the
behaviour of deep copy?

A shallow copy of a List or an array creates a new List or array with
identical references in each entry.

A deep copy of a List or an array creates a new List or array with
references to equivalent but values in each entry. Changes to the
objects referenced by the cloned List/array would not be reflected in
the original, or vice versa.
So, if a shallow copy on a java collection will clone the object
reference to the collection and clone the object reference it contains,
then a deep copy of java collection would be the above plus clone any
object references inside the object in the array. Ex:

We don't usually talk about cloning references, as it's only a bitwise copy.

I think the program below attempts to do the deep vs shallow cloning
test you were attempting.

import java.util.ArrayList;
import java.util.Vector;

public class Dolly implements java.lang.Cloneable {
@Override
public Dolly clone() {
System.out.println("Cloned!");
return new Dolly();
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
{
Dolly[] orig = new Dolly[] { new Dolly() };
Dolly[] clone = orig.clone();
System.out.println(
"Cloned array contents == : "+
(orig[0] == clone[0])
);
}
{
ArrayList<Dolly> orig = new ArrayList<Dolly>();
orig.add(new Dolly());
ArrayList<Dolly> clone = (ArrayList<Dolly>)orig.clone();
System.out.println(
"Cloned ArrayList contents == : "+
(orig.get(0) == clone.get(0))
);
}
{
Vector<Dolly> orig = new Vector<Dolly>();
orig.add(new Dolly());
Vector<Dolly> clone = (Vector<Dolly>)orig.clone();
System.out.println(
"Cloned Vector contents == : "+
(orig.get(0) == clone.get(0))
);
}
}
}

Tom Hawtin
 
M

mingclee1

Hello Tom,

Once again, thanks for your response. The result of your example
prints out true for all three statement. It make sense because it is
doing shallow copy. I actually thought about it the same way, but the
following example proved me wrong.


public class TestClone implements Cloneable {
public Object[] a1 = {new ObjectRef(1)};


public Object clone(){
try{
TestClone result = (TestClone)super.clone();
result.a1 = (Object[])a1.clone();
return result;
}catch(CloneNotSupportedException ne){
return null;
}
}


public static void main(String[] args){
TestClone orig = new TestClone();
TestClone clone = (TestClone)orig.clone();
System.out.println("cloned array contents "+orig.a1[0] ==
clone.a1[0]);
}
}


class ObjectRef{
int val;
public ObjectRef(int _val){
val = _val;
}
public String toString(){
return Integer.toString(val);
}
}


The print out I expected is true, but got false instead. So, it seems
that when cloneing a collection inside an object, it automatically does
deep copy instead of shallow copy. This puzzled me.
 
T

Thomas Hawtin

System.out.println("cloned array contents "+orig.a1[0] ==
clone.a1[0]);
The print out I expected is true, but got false instead. So, it seems
that when cloneing a collection inside an object, it automatically does
deep copy instead of shallow copy. This puzzled me.

This is a puzzler.

It prints (precisely)

false

I want you to look at the statement that generated it and at the
results. Then think carefully. Nasty.

Then I want you to find a copy of Bloch & Gafter's Java Puzzlers in your
local library (bookshop). Find a variation on my ChocolateCake in the
final part of the last puzzler. Compare. ;)

Tom Hawtin
 
M

mingclee1

Already ordered the book. It's on the way. Can't wait to receive it.

I changed to
System.out.println(orig.a1[0] == clone.a1[0]);

and it prints true. Which is what I was looking for. Also, I see
where I was confused. In my first example, instead of
tc.a1[0] = new Entry(100);
I meant
tc.a1[0].val = 100;
and the change should be both reflected in the orig and clone.

Thanks for your help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,050
Latest member
AngelS122

Latest Threads

Top