tags:

views:

10191

answers:

8

Hello,

How can I clone Array List but also clone its items in Java 1.5?

For example I have:

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....

And I would expect that objects in clonedList are not the same as in dogs list.

Thanks for any answer!

+13  A: 

You will need to iterate on the items, and clone them one by one, putting the clones in your result array as you go.

public static List<Dog> cloneList(List<Dog> list) {
    List<Dog> clone = new ArrayList<Dog>(list.size());
    for(Dog item: list) clone.add(item.clone());
    return clone;
}

For that to work, obviously, you will have to get your Dog object to implement the Cloneable interface, and the clone() method.

Varkhan
You can't do it generically, though. clone() is not part of the Cloneable interface.
Michael Myers
But clone() is protected in Object, so you can't access it. Try compiling that code.
Michael Myers
All classes extend Object, so they can override clone(). This is what Cloneable is for!
Stephan202
This is a good answer. Cloneable is actually an interface. However, mmyers has a point, in that the clone() method is a protected method declared in the Object class. You would have to override this method in your Dog class, and do the manual copying of fields yourself.
Jose Chavez
@Jose: Yes, you could do this with a specific class that has overriden clone() to make it public. But you can't do it generically; that's my point.
Michael Myers
@mmyers My bad. Yes, I knew that the Cloneable iface was pretty f***ed up, but I hadn't realized to what extent.
Varkhan
Also, Cloneable is just a signature interface, like Serializable. Even if you overwrite the clone() method in the Object class, if your class does not implement Cloneable, then the javac compiler will throw an error.
Jose Chavez
@Jose: Well, technically, you don't *have* to call super.clone() in your clone() method, so you don't absolutely have to implement Cloneable. See why nobody likes Cloneable? :D
Michael Myers
The idea of the Cloneable interface is that of a ``contract'': a promise to sensibly implement clone(). It is true that Dog needs to override clone() and then implement the interface.
Stephan202
@mmyers You're right. Generically this wont work, because the interface and the method not defined in the same space. Good point.
Jose Chavez
I say, create a factory or builder, or even just a static method, that will take an instance of Dog, and manually copy fields to a new instance, and return that new instance.
Jose Chavez
@JoseChavez Yes, that would be a better design pattern. However the OP mentions cloning, which may be an imposed requirement.
Varkhan
+1 for your refactored method.
Michael Myers
Thanks for really fast answer guys!
palig
+1  A: 

You will need to clone the ArrayList by hand (by iterating over it and copying each element to a new ArrayList), because clone() will not do it for you. Reason for this is that the objects contained in the ArrayList may not implement Clonable themselves.

Edit: ... and that is exactly what Varkhan's code does.

Stephan202
And even if they do, there's no way to access clone() other than reflection, and it's not guaranteed to succeed anyway.
Michael Myers
Varkhan's answer is wrong.
erickson
Let's keep the discussion under Varkhan's post :)
Stephan202
I'm referring to the citation of Varkhan's answer by this answer.
erickson
@erickson: I posted my comment in parallel with yours. Anyway, answer is updated.
Stephan202
+1  A: 

Here's yet another approach, presumably a fast approach: http://javatechniques.com/blog/faster-deep-copies-of-java-objects/

Björn
+8  A: 

I, personally, would add a constructor to Dog:

class Dog
{
    public Dog()
    { ... } // Regular constructor

    public Dog(Dog dog) {
        // Copy all the fields of Dog.
    }
}

Then just iterate (as shown in Varkhan's answer):

public static List<Dog> cloneList(List<Dog> dogList) {
    List<Dog> clonedList = new ArrayList<Dog>(dogList.size());
    for (Dog dog : dogList) clonedList.add(new Dog(dog));
    return clonedList;
}

I find the advantage of this is you don't need to screw around with the broken Cloneable stuff in Java. It also matches the way that you copy Java collections.

Another option could be to write your own ICloneable interface and use that. That way you could write a generic method for cloning.

cdmckay
+1 A copy constructor is the way to go. Also like the idea of an ICloneable interface.
Helper Method
A: 

It was already discussed in Deep clone utility recomendation question

Swiety
A: 

The other posters are correct: you need to iterate the list and copy into a new list.

However... If the objects in the list are immutable - you don't need to clone them. If your object has a complex object graph - they will need to be immutable as well.

The other benefit of immutability is that they are threadsafe as well.

Fortyrunner
A: 

I think the current green answer is bad , why you might ask?

  • It can require to add a lot of code
  • It requires you to list all Lists to be copied and do this

The way serialization is also bad imo, you might have to add Serializable all over the place.

So what is the solution:

Java Deep-Cloning library The cloning library is a small, open source (apache licence) java library which deep-clones objects. The objects don't have to implement the Cloneable interface. Effectivelly, this library can clone ANY java objects. It can be used i.e. in cache implementations if you don't want the cached object to be modified or whenever you want to create a deep copy of objects.

Cloner cloner=new Cloner();
cloner.deepClone(someObject);

Check it out at http://robust-it.co.uk/clone/index.php

One caveat with this method is that it uses reflection, which can be quite a bit slower than Varkhan's solution.
cdmckay
+1  A: 

In addition to the "Java Deep Cloning Library", there's also the utility at http://www.genericdeepcopy.com/. It's aim is to deep copy any Java object (non-Serializable objects), which it does, and which as cdmckay mentioned is slower than custom code.

Aaron