Java Array Copy — Why Arrays.copyOf Corrupted Our Audit Log
Shallow copy let two components mutate the same Order objects, causing duplicate logs.
- Array assignment (b = a) creates an alias, not a copy — both variables share the same memory
- System.arraycopy is the fastest, native-level copy with precise source/destination control
- Arrays.copyOf and copyOfRange are the readable, modern choice — creates the destination array for you
- For primitive arrays, all standard copy methods produce a true independent copy
- For object arrays, every built-in method only copies references — you need a manual deep copy loop
- The biggest mistake: assuming copyOf on an object array gives you independent nested objects
Every real Java program manipulates data, and data lives in arrays. Whether you're building a leaderboard, processing sensor readings, or shuffling a deck of cards, there will come a moment where you need a second copy of an array — one you can modify freely without destroying the original. That moment trips up more developers than you'd expect.
The problem is that Java arrays are objects, and in Java, when you copy an object reference, you don't automatically copy the object itself. Write int[] copy = original; and you haven't made a copy at all — you've given the same array two names. Every change you make through copy silently corrupts original. Java gives you several proper tools to avoid this trap, and each one has a different sweet spot.
By the end of this article you'll be able to use all four mainstream array-copying techniques — assignment (and why it's wrong), System.arraycopy, Arrays.copyOf, and clone — explain the difference between a shallow and a deep copy, and answer the interview question that catches even mid-level developers off guard.
Why `int[] copy = original` Is Not a Copy (The Reference Trap)
Before you learn how to copy an array correctly, you need to understand why the obvious approach fails. In Java, an array is an object that lives in a region of memory called the heap. A variable like int[] scores doesn't hold the array itself — it holds the address of where the array lives, like a sticky note with a house number written on it.
When you write int[] copy = scores, you're not duplicating the house — you're writing the same house number on a second sticky note. Both variables now point to the exact same block of memory. Change an element through copy and you'll see the change through scores too, because they're looking at the same data.
This is called aliasing, and it causes bugs that are notoriously hard to track down because nothing looks wrong at first glance. The fix is to create a brand-new array and then transfer the values across — and Java gives you multiple built-in ways to do exactly that.
The Right Way: System.arraycopy — Fast, Precise, Low-Level
System.arraycopy is the oldest and fastest array-copying method in Java. It's a native method, meaning it's implemented at the JVM level and uses optimised memory operations under the hood. When performance is critical — think copying millions of log entries or processing image pixel data — this is your go-to.
The signature looks intimidating at first: System.arraycopy(source, sourceStart, destination, destStart, length). Break it down: you tell it where to read from (source array and start index), where to write to (destination array and start index), and how many elements to copy. This precision is its superpower — you can copy just a slice of an array into the middle of another one, which none of the other methods let you do as easily.
The destination array must already exist before you call this method. You have to create it yourself with new. That's a bit more ceremony, but it also means you stay in control of the exact size of the result.
Arrays.copyOf and Arrays.copyOfRange — The Readable, Modern Choice
Arrays.copyOf was introduced in Java 6 as a more readable alternative to System.arraycopy. It handles creating the destination array for you, which removes one step and one potential mistake. You just say 'give me a copy of this array with this many elements' and it returns a brand-new array.
If you ask for fewer elements than the original, you get a truncated copy. If you ask for more, the extra slots are filled with the default value for that type — zero for numbers, null for objects, false for booleans. This makes it surprisingly useful for resizing arrays.
Arrays.copyOfRange takes this further — you specify exactly which slice of the original you want, using a start index (inclusive) and end index (exclusive). Think of it like Python's slice notation if you've seen that. Both methods live in java.util.Arrays, so you'll need that import.
Shallow vs Deep Copy — The Gotcha That Catches Everyone
Here's the part that trips up even experienced developers. All the methods above — System.arraycopy, Arrays.copyOf, clone — create what's called a shallow copy. For arrays of primitives (int, double, char, etc.), shallow copy is perfectly fine: primitives are stored by value, so copying them gives you genuinely independent data.
But for arrays of objects, shallow copy only copies the references — those sticky notes with house numbers — not the objects themselves. So if your array holds Student objects, after a shallow copy you have two arrays with separate slots, but every slot in both arrays still points to the same Student object in memory. Change a field on a student through one array and you'll see the change through the other.
A deep copy means duplicating the objects too, not just the references. Java doesn't give you a one-liner for that — you have to copy each object manually, typically in a loop. This is one of the most common interview topics around arrays in Java, so it's worth burning into memory.
Choosing the Right Copy Method: A Decision Framework
With four different ways to copy arrays in Java, picking the right one depends on your specific use case. Here's a decision framework that senior engineers use.
Use assignment (=) — never. There's no scenario where this is correct for copying. It's an alias, not a copy.
Use System.arraycopy when: you need to copy into an existing array, you're inserting a slice into the middle of another array, or you're in a tight loop copying millions of elements and every microsecond counts.
Use Arrays.copyOf when: you want a simple full copy or a resized copy, and readability matters more than absolute micro-optimisation. This covers 80% of everyday use cases.
Use Arrays.copyOfRange when: you need a safe slice without manually computing source indices. It's your best friend for extracting subarrays.
Use clone when: you want a one-liner for a primitive array and you're OK with getting a full copy. It's concise but lacks any resizing or slicing features.
Deep copy when: you have an array of mutable objects and you need independence. There's no built-in one-liner; implement a loop or use a library like Apache Commons Lang3 SerializationUtils.clone for serializable objects.
| Feature / Aspect | System.arraycopy | Arrays.copyOf / copyOfRange | array.clone() |
|---|---|---|---|
| Introduced in | Java 1.0 | Java 6 | Java 1.0 |
| Creates destination array | No — you create it | Yes — returned for you | Yes — returned for you |
| Copy a slice / partial range | Yes — full control | Yes — via copyOfRange | No — always full array |
| Insert into middle of target | Yes | No | No |
| Resize while copying | No | Yes — extend or truncate | No |
| Performance | Fastest (native) | Same (uses arraycopy internally) | Same (uses arraycopy internally) |
| Readability | Lower — more parameters | High — intention is clear | High — very concise |
| Works on multidimensional arrays deeply | No — shallow only | No — shallow only | No — shallow only |
| Best use case | Performance-critical slicing | Everyday full or ranged copies | Quick full copy of primitives |
Key Takeaways
- Assigning one array variable to another (
b = a) never creates a copy — it creates an alias. Both variables point to the same memory, so changes through one are visible through the other. - For primitive arrays, all four methods (System.arraycopy, Arrays.copyOf, Arrays.copyOfRange, clone) produce truly independent copies — primitives are stored by value, so there's no aliasing risk inside the array.
- For object arrays, every standard copy method is a shallow copy — the array slots are independent, but the objects those slots point to are still shared. Mutating an object through the copy mutates the original. A deep copy requires a manual loop that constructs new objects.
- Pick your tool by intent:
System.arraycopyfor performance-critical or partial/offset copies;Arrays.copyOf/Arrays.copyOfRangefor everyday readable copies;for a quick one-liner on simple primitive arrays.clone() - The most dangerous assumption is thinking that
Arrays.copyOfon an object array gives you a deep copy. It doesn't. Always check element mutability before deciding your copy strategy.
Common Mistakes to Avoid
- Using `=` to 'copy' an array
Symptom: You write `int[] backup = data;` expecting a real copy, but any change through `backup` silently mutates `data`. The bug is intermittent and hard to isolate because the alias is invisible in code.
Fix: Always useArrays.copyOf(data, data.length)orSystem.arraycopyto create a genuinely independent array. Never use assignment for copying. - Assuming a shallow copy is safe for object arrays
Symptom: You use `Arrays.copyOf` on a `Student[]` and confidently modify grades in the copy, only to discover the originals have changed too. This causes data corruption in any system that relies on isolation (e.g., caching, event pipelines).
Fix: For arrays of mutable objects, write a deep copy loop that constructs a fresh object for each element, or implement a copy constructor/clone method in your class. - Getting the index bounds wrong in System.arraycopy
Symptom: You pass `weeklyTemps.length` as the `length` argument but forget that `sourceStart` is already 2, which causes an `ArrayIndexOutOfBoundsException` at runtime because you're trying to read past the end of the source.
Fix: The number of elements to copy must satisfysourceStart + length <= source.length. Double-check your arithmetic, or useArrays.copyOfRangewhich handles bounds for you. - Using `clone()` on an object array and thinking it's a deep copy
Symptom: After `Student[] copy = classA.clone()`, modifying `copy[0].grade` also changes `classA[0].grade`. The team assumed clone would deep copy, but it only creates a shallow copy of references.
Fix: Understand thaton arrays does a shallow copy. For deep copy, you must manually copy each element. If elements implementclone()Cloneableand have a propermethod, you can callclone()in a loop.element.clone()
Interview Questions on This Topic
- QWhat is the difference between a shallow copy and a deep copy of an array in Java, and which built-in methods produce each type?SeniorReveal
- QIf I write
String[] copyB = Arrays.copyOf(copyA, copyA.length)and then docopyB[0] = "New Name", doescopyA[0]change? What if I docopyB[0].toLowerCase()— does that affectcopyA[0]?SeniorReveal - QWhen would you choose
System.arraycopyoverArrays.copyOf, and is there any real performance difference between them for large arrays?SeniorReveal - QHow do you deep copy a 2D array in Java?Mid-levelReveal
Frequently Asked Questions
Does Arrays.copyOf create a deep copy in Java?
No. Arrays.copyOf always creates a shallow copy. For primitive arrays this is fine because primitives are copied by value. For object arrays, only the references are copied — both the original and the copy point to the same underlying objects. To get a true deep copy of an object array you need to manually construct new objects in a loop.
What is the fastest way to copy an array in Java?
System.arraycopy is the fastest because it's a native method implemented at the JVM level and uses low-level memory block operations. In practice Arrays.copyOf is just as fast for typical use cases because it delegates to System.arraycopy internally — the difference only becomes measurable at very large scales or in tight loops.
Can I copy a 2D array with Arrays.copyOf?
You can copy the outer array, but the result is still a shallow copy — each element of the outer array is a reference to an inner array, and those inner arrays are shared between the original and the copy. To truly copy a 2D array you need a nested loop: iterate over every row and call Arrays.copyOf (or System.arraycopy) on each row individually to create independent inner arrays.
Is `clone()` on arrays a deep copy?
No. on arrays performs a shallow copy — it creates a new array and copies the references (or values for primitives) from the source. For primitive arrays this is effectively a full copy. For object arrays, the objects themselves are not cloned.clone()
What's the difference between `clone()` and `Arrays.copyOf()`?
Both produce shallow copies. Key differences: copyOf can resize (truncate or extend) the resulting array, while clone always returns an array of the same length. copyOf returns the exact type (e.g., String[]) without casting, whereas clone returns Object for reference arrays. Also, clone is inherited from Object and can be overridden, but array cloning cannot be customised.
That's Arrays. Mark it forged?
5 min read · try the examples if you haven't