ArrayList
Cloning
Java Programming
Data Structures
Deep Copy

How to clone ArrayList and also clone its contents?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Cloning an ArrayList and cloning the objects inside it are two different problems. Copying the list container is easy, but making the contents independent requires a deep-copy strategy for the element type itself.

Understand Shallow Versus Deep Copy

A shallow copy creates a new ArrayList instance that points to the same element objects. A deep copy creates a new list and new element instances.

java
1import java.util.ArrayList;
2import java.util.List;
3
4List<String> original = new ArrayList<>();
5original.add("A");
6original.add("B");
7
8List<String> copy = new ArrayList<>(original);

For immutable types such as String, that shallow copy is usually fine because the elements themselves cannot be changed. The problem becomes important when the elements are mutable objects.

A New ArrayList Does Not Clone the Elements

Suppose the list contains mutable Person objects.

java
1import java.util.ArrayList;
2import java.util.List;
3
4class Person {
5    String name;
6
7    Person(String name) {
8        this.name = name;
9    }
10}
11
12List<Person> original = new ArrayList<>();
13original.add(new Person("Ana"));
14
15List<Person> shallowCopy = new ArrayList<>(original);
16shallowCopy.get(0).name = "Mina";
17
18System.out.println(original.get(0).name); // Mina

The list containers are different, but both lists still refer to the same Person instance. That is why the original list appears to change.

Build a Deep Copy by Copying Each Element

To make the contents independent, you need a way to copy each object. A copy constructor is often clearer and safer than relying on clone().

java
1import java.util.ArrayList;
2import java.util.List;
3
4class Person {
5    private final String name;
6
7    Person(String name) {
8        this.name = name;
9    }
10
11    Person(Person other) {
12        this.name = other.name;
13    }
14
15    public String getName() {
16        return name;
17    }
18}
19
20List<Person> original = new ArrayList<>();
21original.add(new Person("Ana"));
22original.add(new Person("Ben"));
23
24List<Person> deepCopy = new ArrayList<>();
25for (Person person : original) {
26    deepCopy.add(new Person(person));
27}

Now the copied list contains distinct Person objects. Changes to elements in one list do not affect the other.

Streams Can Make the Copying Intent Clearer

If you prefer a more functional style, Java streams can map each original element to a copied element.

java
1import java.util.List;
2import java.util.stream.Collectors;
3
4List<Person> deepCopy = original.stream()
5    .map(Person::new)
6    .collect(Collectors.toList());

This is concise, but the important part is still the same: the element type knows how to copy itself.

Why clone() Is Often Not the Best Design

ArrayList.clone() creates only a shallow copy of the list structure. It does not recursively clone the contents.

java
@SuppressWarnings("unchecked")
ArrayList<Person> shallowCopy = (ArrayList<Person>) ((ArrayList<Person>) original).clone();

That may be enough for immutable elements, but it does not solve deep-copy requirements. More broadly, Java's Cloneable pattern is often awkward because it gives weak guarantees and pushes a lot of subtle behavior into Object.clone(). In many codebases, copy constructors or static factory methods are easier to understand and test.

If the element itself contains nested mutable fields, the copy constructor has to copy those too. Otherwise you may deep-copy the list but still leave shared mutable state inside each element, which only moves the bug one level deeper.

Common Pitfalls

Assuming new ArrayList<>(original) or clone() deep-copies the contents is the core mistake. It only copies the container.

Using shallow copies with mutable element types leads to surprising shared state between lists.

Reaching for clone() before deciding how each element should actually be copied often creates more confusion than clarity.

Summary

  • Copying an ArrayList and deep-copying its contents are separate tasks.
  • 'new ArrayList<>(original) and ArrayList.clone() perform shallow copies of the list structure.'
  • For mutable element types, implement an explicit copy strategy such as a copy constructor.
  • Use deep copies only when you truly need independent mutable objects.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.