Java Array Copy: Methods, Behavior, and Pitfalls
java array copy: Learn how to copy arrays in Java correctly. Compare System.arraycopy, Arrays.copyOf, clone, and manual loops, and understand shallow vs deep copy.
When you assign one array variable to another, you are not copying the array. You are copying the reference. Both variables point to the same array object in memory, so any modification through one variable is visible through the other. For example, int[] a = {1, 2, 3}; int[] b = a; b[0] = 99; changes a[0] as well. This behavior is often the root cause of subtle bugs, and it is why an explicit java array copy operation is needed in most real-world code.
Why Assignment Does Not Copy an Array
Java arrays are objects, and variables of array type hold references to those objects. The assignment operator copies the reference, not the underlying elements. This is true for primitive arrays and object arrays alike. If you need a separate array that can be modified independently, you must create a new array and copy the elements into it.
Consider this common mistake:
int[] original = {1, 2, 3}; int[] alias = original; alias[1] = 42; System.out.println(original[1]); // prints 42
The output is 42 because alias and original refer to the same array. To preserve the original data, you need a real copy. Java provides several mechanisms, each with different tradeoffs.
Using System.arraycopy for Native-Speed Copies
System.arraycopy is a native method that copies a range of elements from a source array to a destination array. It is the lowest-level copy operation available in the standard library and is typically the fastest for bulk copies because it can use optimized native memory operations.
The method signature is:
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
It requires an existing destination array with sufficient capacity. It does not create a new array. You must allocate the destination yourself.
int[] source = {1, 2, 3, 4, 5}; int[] target = new int[source.length]; System.arraycopy(source, 0, target, 0, source.length);
This copies all elements from source into target. You can also copy a subrange by adjusting the position and length parameters. The method throws IndexOutOfBoundsException if the source or destination range is invalid, and ArrayStoreException if the source and destination types are incompatible.
Because System.arraycopy does not allocate a new array, it is useful when you want to reuse a pre-allocated buffer or copy into a larger array at a specific offset. It is also the underlying implementation for many higher-level copy methods.
Using Arrays.copyOf and Arrays.copyOfRange
The Arrays utility class provides convenience methods that allocate a new array and copy elements into it. Arrays.copyOf copies the specified array and returns a new array of the given length. If the new length is larger, the extra positions are filled with default values (0, false, null). If it is smaller, the copy is truncated.
int[] original = {1, 2, 3}; int[] copy = Arrays.copyOf(original, original.length);
Arrays.copyOfRange copies a range of the original array into a new array. The from index is inclusive, the to index is exclusive.
int[] original = {1, 2, 3, 4, 5}; int[] part = Arrays.copyOfRange(original, 1, 4); // {2, 3, 4}
These methods are implemented internally using System.arraycopy, so they offer the same native performance while handling array allocation for you. They are the most readable and idiomatic choice for creating a copy in a single expression.
Using clone for a Simple Copy
Every array type implements Cloneable and overrides clone(). Calling clone() on an array returns a new array that is a shallow copy of the original. For primitive arrays, this is a perfect copy of the values. For object arrays, the references are copied, not the objects themselves.
int[] original = {1, 2, 3}; int[] copy = original.clone();
The clone() method is concise and requires no explicit length calculation. However, it is not as flexible as Arrays.copyOf because you cannot control the length or copy a subrange. Also, the return type is the same as the array type, so no cast is needed.
For most use cases, clone() is a fine choice when you want a straightforward shallow copy and do not need to adjust the length. It is slightly less explicit about the copy operation than Arrays.copyOf, but it is a standard Java idiom.
Shallow vs Deep Copy for Object Arrays
When an array contains objects, a shallow copy copies the references. The new array points to the same objects as the original. Modifying an object through one array is visible through the other. If you need independent copies of the objects themselves, you must perform a deep copy.
String[] original = {"a", "b"}; String[] shallow = original.clone(); shallow[0] = "c"; // does not affect original[0] // But if the elements were mutable objects, changes would be shared.
For immutable objects like String, shallow copy is effectively safe. For mutable objects, you need to copy each element individually. There is no built-in deep copy method for arrays in the standard Java library. You must create a new array and populate it with copies of the elements, either by calling a copy constructor, using a factory method, or serializing and deserializing (which is heavy and not recommended for most cases).
class Point { int x, y; Point(int x, int y) { this.x = x; this.y = y; } Point(Point p) { this.x = p.x; this.y = p.y; } } Point[] original = {new Point(1, 2), new Point(3, 4)}; Point[] deep = new Point[original.length]; for (int i = 0; i < original.length; i++) { deep[i] = new Point(original[i]); }
A deep copy is more expensive and requires careful handling of nested objects and cycles. In practice, you should only deep copy when you actually need to isolate the object graph. For arrays of immutable objects or primitives, a shallow copy is sufficient.
Performance and Memory Considerations
The performance of array copy methods is dominated by the amount of data being copied and the memory allocation required. System.arraycopy is the fastest because it is a native call and does not allocate a new array when you provide the destination. Arrays.copyOf and clone allocate a new array and then call System.arraycopy internally, so they have the same copying speed but add allocation overhead.
For large arrays, allocation can be a significant cost. If you are copying arrays repeatedly in a loop, reusing a destination buffer with System.arraycopy avoids repeated allocation and can reduce garbage collection pressure. On the other hand, Arrays.copyOf is more readable and is usually the right choice when you need a new array anyway.
There is no built-in way to copy an array in parallel using the standard library. For very large arrays, you might consider manual parallel copying using ForkJoin or the Streams API, but the overhead of parallelism often outweighs the benefit unless the array is extremely large and the copy operation is a bottleneck.
Another consideration is type compatibility. System.arraycopy can copy between arrays of different but compatible types, such as from a String[] to an Object[], because it performs a runtime type check for each element. Arrays.copyOf and clone preserve the exact array type, which is safer and avoids ArrayStoreException.
Choosing the Right Array Copy Method
The choice depends on whether you need a new array, a subrange, or a pre-existing destination. The following table summarizes the main options:
| Method | Allocates New Array | Copies Subrange | Destination Control | Type Preservation |
|---|---|---|---|---|
System.arraycopy | No | Yes | Full control | Runtime check |
Arrays.copyOf | Yes | No | Length only | Exact type |
Arrays.copyOfRange | Yes | Yes | No | Exact type |
clone() | Yes | No | No | Exact type |
Use System.arraycopy when you have a pre-allocated destination buffer or need to copy into a specific position. Use Arrays.copyOf when you want a new array of a given length, possibly with truncation or default padding. Use Arrays.copyOfRange to extract a subrange. Use clone() for a simple, concise shallow copy of the entire array.
For object arrays, remember that all these methods perform a shallow copy. If you need deep copy, you must implement it manually. Also be aware that copying a String[] with clone() is safe because String is immutable, but copying a List[] or an array of custom mutable objects requires extra care.
In production code, prefer the most explicit method that matches the intent. Arrays.copyOf is often the clearest for creating a defensive copy of an array before storing it or returning it from a method. System.arraycopy is best when performance is critical and you are managing buffers manually. clone() is acceptable when the array type is final and the copy is straightforward, but it is less explicit about the copy operation.
A final edge case: when copying a multidimensional array, all standard methods perform a shallow copy of the top-level array. The nested arrays are shared. To copy a multidimensional array deeply, you must copy each sub-array individually. This is a common source of bugs when developers assume clone() or Arrays.copyOf creates a fully independent array.
The key is to understand that a Java array copy is always a shallow copy unless you explicitly copy the elements. Knowing this, you can choose the appropriate method for your scenario and avoid the aliasing bugs that come from assigning array references directly.