I am trying to code my own version of ArrayList (to learn how it works) and I was looking at the Cloneable interface which is implemented by java.lang.ArrayList (I would like to keep the same contract than the original version).
I am a bit confused by the definition of the clone() method and hope someone can clarify it for my case.
The part I am not really sure of, is that if someone use the clone() method on my collection, it should return a new ArrayList (meaning not a reference to the existing one).
However:
Does it means that each object contained needs to be cloned as well?
Do I have to explicitly create a new instance using 'new' for each of them (which should be quite slow?)
Should I be trying to use the clone() method on my collection's objects, and how to be sure they override the clone() method as the definition in Cloneable states that it is not explicitly required (the method is not in the Interface)?
Thanks in advance for any help.
In the context of a Collection the contract is to get a new Collection with the same objects, not copies or clones of the objects. Anything else would break the contract.
So what you want is a "shallow" copy of the elements, just their references, in the new collection. What you are suggesting with the cloning/new is a "deep copy" which is very different, and you shouldn't do in this case.
So
You definitely should not clone each of the objects in the Collection.
Same as 1. You should not use new, this would be doing a "deep" copy of the collection.
No, same as 1. 2.
Related
I'm working on some code and trying to figure out how to copy an object reference to another object. I keep seeing the clone() method used, but I've read it's flawed and the class I'm wanting to copy is already implementing serializable. So one question I have is about the difference between using '=' and the clone method - My understanding is that both of these are shallow copies so they should work the same, but if that's true, then what is the benefit of having a method for this?
The code I'm working on has 2 arrays of objects - both the same type of object, all objects in both arrays are initially null, and I assign the values of array B to equal array A.
As the code progresses objects in array A are initialized and values are assigned to the variables of the objects in array A.
At the end of the code though, all objects in array B are still null.
Do I have the concept wrong here?
If the concept isn't wrong I assume it's just something I'm overlooking in my code.
Any help is appreciated.
I'll make my comment an answer:
Your question is comparing apples to oranges, they are so completely different that they can't be compared. = assigns a reference, that's it. Clone creates a completely new object, one whose state should be the same as the cloned object, but again it is a completely different object/reference. As an aside, there are deep and shallow clones, and so the composite fields of shallow copied clones may be identical, but that's the subject of another question.
When you use =, you copy the reference of the object (in memory). When using .clone(), you create a new object.
I am trying to clone an object of MyGraph and I want it to be a deep copy so the arraylists inside the object are also cloned. Right now I have:
public static MyGraph deepCopy(MyGraph G){
MyGraph Copy = (MyGraph) G.clone();
Copy.VertexG = (ArrayList<Integer>) G.VertexG.clone();
Copy.EdgeG = (ArrayList<String>) G.EdgeG.clone();
return Copy;
}
This returns an error when it tries to clone the arraylist. I am not sure if this is the right way to add the arraylists to the object.
The clone operation in ArrayList returns a shallow copy of the object, and will not be suitable for your purposes. The manual workaround is to:
Create a target array list of the same size as the source list
Iterate the source list and create a clone of each of it's items, into the target list
Obviously, this will only work if the array list contains items that implement clone, and in addition that the items clone operation actually returns a deep copy. In other words, its not guaranteed. Actually, implementing deep clone functionality for Java objects is not at all easy, refer to extensive discussions in Java: recommended solution for deep cloning/copying an instance and other SO threads to get a feel for the options available. In addition to the answers provided there, here are some other options:
Serialization
If all (the required) objects in your hierarchy can be serialized then you can use this simple code to do a deep clone:
public MyGraph deepCopy() {
try {
final ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
final ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
oos.close();
final ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(baos.toByteArray()));
final MyGraph clone = (QuicksortTest) ois.readObject();
return clone;
} catch (final Exception e) {
throw new RuntimeException("Cloning failed");
}
}
Note that some deep-clone libraries combine standard Java serialization with reflection hacks and/or byte code instrumentation in order to make the entire object hierarchy fully serializable. You may, or may not, need that.
Copy tools
For example, Dozer, provide fast deep-copy functionality. Orika can also achieve the same, albeit with more configuration:
public MyGraph deepCopy() {
final DozerBeanMapper mapper = new DozerBeanMapper();
final QuicksortTest clone = mapper.map(this, MyGraph.class);
return clone;
}
The only downside of course, being the additional dependencies you need to pull into your project.
On a total tangent, your deepCopy method should not be static. Also, you should seriously considering encapsulating the state of your object by making it private and implementing getters/setters.
Every class you call clone() on has to implement the Cloneable interface. From your comments, i understand your MyGraph class does not implement the Cloneable interface. In that case, Object.clone() throws the CloneNotSupportedException.
Trying to do deep copy with cloning is complicated as you need to ensure that all classes implement Cloneable interface and they have clone() definition.
Better way would be to do it through Copy Constructor or Serialization. Here is my blog on which i have discussed it in detail. hope it helps :)
A fundamental conceptual problem with cloning in Java [arguably the fundamental problem] is it's possible for a field of a type like List<String> to represent at least five very different things:
The only extant reference to a mutable list, which is used to encapsulate the mutable state thereof, but which--being the only extant reference--would not encapsulate its identity (the list could be replaced with a different list holding the same items, without altering the program's semantics). A correct clone of the object that contains this field would hold a reference to a different list holding the same items.
A reference to a mutable list which, while it would allow itself to be mutated, will never be exposed to anything that would actually mutate it. This reference may be shared with other code only if that other code will refrain from mutating the list or exposing it to code that might do so. A correct clone of the object that contains this field could hold a reference to either the original list or a different list holding the same items.
A reference to an immutable list. This reference may be shared freely with other code without regard for how that code might expose it. As above, the correct clone of the object containing this field could hold a reference to either the original list or a copy.
A reference to a mutable list which is owned by some other object, which is held for purpose of binding this to those aspects of the other object's state which are encapsulated in the list. A correct clone of the object holding the field must hold a reference to that same list, and not a copy thereof.
A reference to a mutable list which this object owns, but to which other objects also have a reference for purpose of either observing this object's state, or feeding information to this object. The object holding this field cannot be correctly cloned in isolation, though it might be possible to clone a group of inter-connected objects and give the new set of objects a set of interconnections which was isomorphic to those in the original group.
The concrete type of the object to which the field holds a reference may distinguish between some of the above cases, but it cannot distinguish among all of them. In particular, the first and fourth scenarios require different behavior on the part of the cloning method, despite the fact that in both scenarios the reference might likely pointing to an ArrayList<string>.
I have a treeSet of custom objects. Each custom object is made up of int a, int b, String c and double d. Lets say I have a treeSet object t1 containing 10 such custom objects. I also have another TreeSet object t2 which is empty. what is the best way of copying the objects in treeset t1 into treeset t2... I want new objects in t2 and not just refernces to the ones in t1.one way is to create 10 new objects in t2 and copy the values of all the a's and b's and c's and d's of each of the 10 objects in t1 to those in t2. Any better way?
This technique is known as "deep copying" and there's a good Stack Overflow question on it here.
The current top two answers provide two good perspectives:
Serialize your objects and then deserialize them (efficient, but not 100% reliable) -- link to answer
or you'll just have to traverse the whole object and do it manually (as reliable as you can get but not super-simple to do) -- link to answer
for (Item item : collection) {
newCollection.add(BeanUtils.cloneBean(item));
}
where BeanUtils is from commons-beanutils
Override the clone() method in your custom object
Iterate over the source collection
Add the cloned items to the destined collection.
Depends on what you mean by better. The best solution is to make the object immutable and just copy the references.
An alternative is to generate or write a copy constructor. The copy constructor can use reflections to copy all the fields.
For the field types you have, you can make the Object Cloneable and use the clone() methods. (Because the fields are primitives or immutable)
You already got your answer.
However this process is best to be implemented in the clone() method. So all your object should overwrite this method.
See an example here: http://www.roseindia.net/java/example/java/util/clone-method-example-in-java.shtml
I understand that in order to copy an arraylist and have two lists independent of one another you must use a deep copy (copying the objects from one list to another not just the references), however is there a way this can be done cross-class?
For example; I am calling Class2 from Class1. In Class2 objects are added to an ArrayList of custom objects upon an event. I would like to be able to transfer this ArrayList to Class1 but whenever I try I get a NullPointer.
Any clues??
This is highly indicative of a design flaw.
See if you can't accomplish the same goal by wrapping your list in a class, sharing the class and using it to control access to the list.
The only case where this wouldn't just outright work is if your two classes must modify the list independently.
If this is a requirement, then I would probably hand a different instance of the wrapper class to each modifying class (with a reference to the same source list), then have a way for newly added data to be tagged with an ID referring to the original class--that way when you query, the wrapper would only return untagged items (items that were part of the original shared list) and items tagged with it's own ID.
Either that or the wrapper class could contain a second list and when queried, return the combined results of the original and second lists.
I've almost never wanted a deep copy. It happens, but it's quite rare.
If you post more info, maybe we can be more specific in helping with the redesign.
ALSO: (Edit)
Chances are that the copied array list isn't your problem--it's probably that it wasn't TRULY a deep copy. For a deep copy, it means you implement a copy method (I believe they are supposed to be called .clone(), I never use this stuff--as I said, it's bad juju) for each object in the array list, then you call your copy method on each one to get the new copy in your next list.
Furthermore, any objects referenced by your copied object should probably be cloned as well. "Deep" means all the way down the tree.
I'm guessing you're failing somewhere in the process.
I'd really like to hear why you feel you need a copy instead of a reference.
My suggestion is for you to create a getArray() method and call it from the other class. This method should create a copy of the ArrayList because you should not "transfer" variables within classes; always with get() method so OO paradigm stays intact.
Do something like this:
Class 1
public ArrayList<Object> getArray() {
ArrayList<Object> aux = new ArrayList<Object>();
for(Object x : list) //object is the string, int, etc...
aux.add(x.clone()) //assuming the Object has a clone method!
return aux;
}
On Class 2, just call this method. Then just look at the test from the other answer about the null exception, should work.
Hope it helps.
In Java, say you have a class that wraps an ArrayList (or any collection) of objects.
How would you return one of those objects such that the caller will not see any future changes to the object made in the ArrayList?
i.e. you want to return a deep copy of the object, but you don't know if it is cloneable.
Turn that into a spec:
-that objects need to implement an interface in order to be allowed into the collection
Something like ArrayList<ICloneable>()
Then you can be assured that you always do a deep copy - the interface should have a method that is guaranteed to return a deep copy.
I think that's the best you can do.
One option is to use serialization. Here's a blog post explaining it:
http://weblogs.java.net/blog/emcmanus/archive/2007/04/cloning_java_ob.html
I suppose it is an ovbious answer:
Make a requisite for the classes stored in the collection to be cloneable. You could check that at insertion time or at retrieval time, whatever makes more sense, and throw an exception.
Or if the item is not cloneable, just fail back to the return by reference option.