Explain Codes LogoExplain Codes Logo

Java ArrayList copy

java
deep-copy
shallow-copy
clone
Anton ShumikhinbyAnton Shumikhin·Aug 29, 2024
TLDR

To create an exact duplicate of an ArrayList in Java, you use the special Java syntax new ArrayList<>(originalList). A new ArrayList object will be created, and it will contain the same elements as the original list.

If the list you're duplicating contains objects, be aware this is a shallow copy, implying the objects' references are duplicated, not the objects themselves. Essentially, the copied list points to the same instances of the objects.

ArrayList<String> copiedList = new ArrayList<>(originalList);

If you wish to duplicate the content of the objects as well (aka create a deep copy), you will need to manually perform this operation, as the clone() function of ArrayList doesn't copy the objects' content.

Types of copying: Shallow vs Deep

Shallow copy: clone()

An alternate strategy to create a shallow copy involves using the clone() function, which replicates the list's structure but not its objects.

@SuppressWarnings("unchecked") ArrayList<String> clonedList = (ArrayList<String>) originalList.clone();

Note: clone() must be used with care. Since it is not type-safe, misuse could lead to a ClassCastException at runtime.

Shallow copy: addAll()

The addAll() function can append all the existing list's elements into a new list, effectively creating a simple shallow copy.

ArrayList<String> listToAdd = new ArrayList<>(); listToAdd.addAll(originalList); //// A tisket, a tasket, I'll add all to the basket!

Shallow copy: Collections.copy()

For using Collections.copy(), the destination list needs to be initialized with the correct size. This can be accomplished through the combination of Collections.copy() and Collections.nCopies().

List<String> destList = new ArrayList<>(Collections.nCopies(originalList.size(), (String)null)); //// nulling it before copying it! Collections.copy(destList, originalList); //// Know what they say - copy-paste is the soul of programming!

Deep copy: Manual or Cloneable or Serialization

A deep copy requires additional steps, such as:

  • A loop to manually copy individual elements.
  • Using the individual elements' copy constructor or clone function if the elements implement the Cloneable interface.
  • Employing serialization techniques to deeply copy objects.
// Example of a deep copy using a manual loop for Clonable objects ArrayList<MyObject> deepCopiedList = new ArrayList<>(); for (MyObject item : originalList) { deepCopiedList.add(item.clone()); //// Watch me clone those sheep! }

Mutable elements in an ArrayList

Mutable objects dilemma

Remember, a shallow copy that contains mutable objects will have the mutation effects ripple through both lists as they both refer to the same underlying objects. Therefore, it is crucial to understand your data types and whether they are mutable (like StringBuilder) or immutable (like String).

Deep copies to the rescue

A deep copy becomes necessary when working with mutable objects in an ArrayList. It ensures data integrity and prevents unintended changes that could lead to hard-to-debug issues.

Nested collections require nested copying

Nested ArrayLists, i.e., an ArrayList containing other collections (like ArrayList<ArrayList<String>>), require both outer and inner list copies to be truly independent from the original.

Performance considerations for copying

Time is money...or performance

Both shallow and deep copies have time overheads. While a shallow copy has a time complexity of O(n), deep copying could be much more expensive depending on the complexity of the objects being copied.

The space-time trade-off

While a deep copy might spare you from quirky bugs due to inadvertent mutations, it occupies more memory as it replicates each element's data. A shallow copy, on the other hand, is more memory-friendly as it reuses the existing objects.