Java Array Clone: Copying Arrays Correctly
java array clone: Understand how to clone Java arrays correctly: the behavior of clone(), System.arraycopy(), and Arrays.copyOf(), and when shallow copy is not enough.
When you need to copy an array in Java, the clone() method is the first thing most developers reach for. A java array clone call is simple to write, but its behavior has a few subtleties that matter when the array holds objects rather than primitives. The choice between clone(), System.arraycopy(), and Arrays.copyOf() depends on what you need the copy for.
The Behavior of clone() on Java Arrays
Calling clone() on an array returns a new array with the same length and the same element values. For primitive arrays, each element is copied by value, so the new array is fully independent:
int[] original = {1, 2, 3}; int[] copy = original.clone(); copy[0] = 99; System.out.println(original[0]); // 1
The array object itself is new. Changing an element in the clone does not affect the original. This is the behavior most developers expect when they call clone().
For arrays of reference types, the situation is different. The new array contains references to the same objects as the original array. The array object is new, but the elements are shared.
Why clone() Is Not Always Enough
Consider an array of mutable objects:
StringBuilder[] original = {new StringBuilder("a")}; StringBuilder[] copy = original.clone(); copy[0].append("b"); System.out.println(original[0]); // ab
The clone shares the StringBuilder instance with the original array. Mutating the object through the clone is visible through the original. This is a shallow copy, and it is the core limitation of clone() on reference-type arrays.
If the array elements are immutable, such as String or boxed primitives, this sharing is harmless because the objects cannot change after creation. The shallow copy behaves like a deep copy in practice.
System.arraycopy() for Copying into Existing Arrays
System.arraycopy() copies a range of elements from a source array into a destination array. Both arrays must already exist, and the destination must have enough space:
int[] source = {1, 2, 3, 4, 5}; int[] dest = new int[5]; System.arraycopy(source, 0, dest, 0, source.length);
The method takes five arguments: the source array, the source starting position, the destination array, the destination starting position, and the number of elements to copy. It is a native method, which makes it the fastest way to copy array segments in the JVM.
System.arraycopy() also handles overlapping ranges correctly when the source and destination are the same array, which is useful for shifting elements within an array.
Arrays.copyOf() for Creating New Arrays
Arrays.copyOf() creates a new array and copies elements into it. It is the convenience method for the common case where you want a new array of the same type:
int[] original = {1, 2, 3}; int[] copy = Arrays.copyOf(original, original.length);
The second argument is the new length. If it is larger than the original, the extra positions are filled with the default value for the element type (zero for numbers, null for references). If it is smaller, the copy is truncated.
Internally, Arrays.copyOf() calls System.arraycopy(), so there is no meaningful performance difference for same-length copies. The choice between them is about whether you already have a destination array to reuse.
Shallow vs Deep Copy: When It Matters
The decision between a shallow and a deep copy depends on whether the array elements are mutable objects. For primitives and immutable types, a shallow copy is sufficient. For mutable objects, you must decide whether sharing the objects is acceptable.
If the cloned array will be used in a separate context where elements may be modified, a deep copy is required. A simple loop can build one:
MyObject[] original = {new MyObject(1)}; MyObject[] deep = new MyObject[original.length]; for (int i = 0; i < original.length; i++) { deep[i] = new MyObject(original[i].getValue()); }
This assumes the element type has a way to construct a new instance from the original. If the class does not expose a copy constructor or a factory method, a deep copy requires a different strategy, such as serialization or a manual field-by-field copy.
Performance and Memory Considerations
System.arraycopy() is a native method, so it is the fastest option for bulk copying. clone() is also efficient because the JVM can optimize it as a native array copy. Arrays.copyOf() delegates to System.arraycopy() internally, so the copying cost is the same.
The main cost to consider is allocation. clone() and Arrays.copyOf() allocate a new array on every call. System.arraycopy() writes into an existing array, so if you copy frequently in a loop, reusing a destination array avoids repeated allocation and garbage collection pressure.
For very large arrays, the copy itself is a memory bandwidth operation. There is no way to avoid reading every element, so the practical optimization is to minimize allocation and to copy only the range you actually need.
Common Pitfalls
One common mistake is assuming clone() on a multidimensional array produces a deep copy. For a 2D array, clone() copies the outer array, but the inner arrays are shared:
int[][] original = {{1, 2}, {3, 4}}; int[][] copy = original.clone(); copy[0][0] = 99; System.out.println(original[0][0]); // 99
To copy a multidimensional array deeply, you must copy each inner array explicitly, for example with a loop that calls clone() on every row.
Another pitfall is expecting clone() on an array to invoke any copy logic defined on the element class. It does not. The array clone() is a shallow copy of the references, regardless of whether the element class overrides clone() or provides a copy constructor.
A third edge case is copying an array of a custom type that holds internal state. Even if the element class has a copy constructor, clone() and System.arraycopy() will not use it. Only an explicit element-by-element copy will produce a true deep copy.