Explain Codes LogoExplain Codes Logo

How to clone ArrayList and also clone its contents?

java
clone
deep-copy
object-cloning
Nikita BarsukovbyNikita Barsukov·Sep 9, 2024
TLDR

This involves creating a deep clone of an ArrayList in Java, by iterating over its elements and creating an instance clone of each. The instances in your list ought to have a method named clone() defined in their classes. Check out this accurate for-loop that creates a clone of your list:

ArrayList<MyCloneable> original = // ... assemble your Avengers here; ArrayList<MyCloneable> cloned = new ArrayList<>(original.size()); // a new playground for (MyCloneable hero: original) { cloned.add(hero.clone()); // clones assemble! }

Group Message: RealAvengersMyCloneable must sign the Cloneable treaty and override clone() to execute a deep copy of its properties.

Customization and alternatives

Tailoring an ICloneable interface

You can tailor your own ICloneable interface for more cloning control:

public interface ICloneable<T> { T clone(); // return of the clone } class MyObject implements ICloneable<MyObject> { // this is the way @Override public MyObject clone() { // logic for deep copy, not for the faint-hearted } }

Streamlining with Stream API

If you're on friendly terms with Java 8 or higher, streams can clean up your code:

ArrayList<MyCloneable> cloned = original.stream() .map(MyCloneable::clone) // may the force be cloned .collect(Collectors.toCollection(() -> new ArrayList<>(original.size()))); // to clonefinity and beyond!

Joda Wisdom: CloneMaster MyCloneable's clone() should not host checked exceptions.

Copy constructors to the rescue

Copy constructors can provide crystal-clear control over object creation:

class MyObject { MyObject(MyObject fellowClone) { // copy fields from 'fellowClone', make the clone strong } } ArrayList<MyObject> cloned = new ArrayList<>(original.size()); // fresh clone territory original.forEach(item -> cloned.add(new MyObject(item))); // cloning spree unleashed

Next-level cloning

Serialization for super-deep cloning

Serialization can produce a clone capable of handling complex object graphs without the need for each class to provide its own clone method:

ByteArrayOutputStream bos = new ByteArrayOutputStream(); new ObjectOutputStream(bos).writeObject(original); // get serialized or die tryin' ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); // born again via bytes ArrayList<MyObject> cloned = (ArrayList<MyObject>) new ObjectInputStream(bis).readObject(); // welcome to the clone club

Major Caution: Every Object must serve Serializable, and this path is infamous for being slower than its competitors.

Initializing the new ArrayList with the same size optimizes performance minimizing the need for array resizing:

ArrayList<MyObject> cloned = new ArrayList<>(original.capacity());

Dare to avoid the pitfalls

  1. Risks of modification: Never alter the original list during cloning. Unexpected consequences guaranteed.
  2. Shared references: Make sure cloned objects are completely independent. No sharing!
  3. Exception handling: Keep 'try-catch' away from clone methods for cleaner API.