Home Java Copying Arrays in Java: Shallow vs Deep Copy Explained

Copying Arrays in Java: Shallow vs Deep Copy Explained

In Plain English 🔥
Imagine you have a recipe card. If you photocopy it, you get a second card — but if the recipe says 'see attachment', both cards still point to the same attachment. That's a shallow copy: two cards, one shared attachment. A deep copy would duplicate the attachment too, so changing one never affects the other. In Java, arrays work the same way — copying the array isn't always the same as copying everything inside it.
⚡ Quick Answer
Imagine you have a recipe card. If you photocopy it, you get a second card — but if the recipe says 'see attachment', both cards still point to the same attachment. That's a shallow copy: two cards, one shared attachment. A deep copy would duplicate the attachment too, so changing one never affects the other. In Java, arrays work the same way — copying the array isn't always the same as copying everything inside it.

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.

ReferenceVsCopy.java · JAVA
1234567891011121314151617181920
public class ReferenceVsCopy {
    public static void main(String[] args) {

        // Original array of top-3 game scores
        int[] originalScores = {1500, 2300, 900};

        // THIS IS NOT A COPY — both variables point to the same array in memory
        int[] aliasScores = originalScores;

        // We think we're only changing aliasScores...
        aliasScores[0] = 9999;

        // ...but originalScores is also changed, because they share the same memory
        System.out.println("originalScores[0] = " + originalScores[0]); // Surprise!
        System.out.println("aliasScores[0]    = " + aliasScores[0]);

        // Proof they point to the same object
        System.out.println("\nSame object? " + (originalScores == aliasScores));
    }
}
▶ Output
originalScores[0] = 9999
aliasScores[0] = 9999

Same object? true
⚠️
Watch Out:The `==` operator on arrays checks if two variables point to the same object in memory — it does NOT compare the contents. To compare contents, use `Arrays.equals(arrayA, arrayB)`.

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.

SystemArrayCopyDemo.java · JAVA
1234567891011121314151617181920212223242526272829303132333435363738
import java.util.Arrays;

public class SystemArrayCopyDemo {
    public static void main(String[] args) {

        // Weekly temperature readings in Celsius
        int[] weeklyTemps = {18, 21, 19, 23, 25, 22, 20};

        // --- Full copy ---
        // Step 1: Create a new array of the same length
        int[] fullCopy = new int[weeklyTemps.length];

        // Step 2: Copy all elements from weeklyTemps into fullCopy
        // Args: (source, sourceStartIndex, destination, destStartIndex, numberOfElements)
        System.arraycopy(weeklyTemps, 0, fullCopy, 0, weeklyTemps.length);

        // Modifying fullCopy does NOT affect weeklyTemps
        fullCopy[0] = 999;

        System.out.println("Original temps : " + Arrays.toString(weeklyTemps));
        System.out.println("Full copy      : " + Arrays.toString(fullCopy));

        // --- Partial copy (just the weekday readings, indices 0-4) ---
        int[] weekdayTemps = new int[5];

        // Copy 5 elements starting at index 0 of weeklyTemps into weekdayTemps at index 0
        System.arraycopy(weeklyTemps, 0, weekdayTemps, 0, 5);

        System.out.println("\nWeekday temps  : " + Arrays.toString(weekdayTemps));

        // --- Inserting a slice into the middle of another array ---
        int[] dashboard = new int[10]; // pre-filled with zeroes
        // Place the weekend temps (indices 5 and 6) into positions 3 and 4 of dashboard
        System.arraycopy(weeklyTemps, 5, dashboard, 3, 2);

        System.out.println("Dashboard      : " + Arrays.toString(dashboard));
    }
}
▶ Output
Original temps : [18, 21, 19, 23, 25, 22, 20]
Full copy : [999, 21, 19, 23, 25, 22, 20]

Weekday temps : [18, 21, 19, 23, 25]

Dashboard : [0, 0, 0, 22, 20, 0, 0, 0, 0, 0]
⚠️
Pro Tip:Use `System.arraycopy` when you need to copy a slice of one array into a specific position in another array. No other single method handles that scenario as cleanly, and it's the fastest option for large arrays in performance-sensitive code.

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.

ArraysCopyOfDemo.java · JAVA
123456789101112131415161718192021222324252627282930313233
import java.util.Arrays;

public class ArraysCopyOfDemo {
    public static void main(String[] args) {

        String[] studentNames = {"Alice", "Bob", "Carol", "David", "Eve"};

        // --- Arrays.copyOf: full copy ---
        // Creates a new array with all 5 names
        String[] fullRoster = Arrays.copyOf(studentNames, studentNames.length);
        System.out.println("Full roster    : " + Arrays.toString(fullRoster));

        // --- Arrays.copyOf: truncated copy (first 3 only) ---
        String[] topThree = Arrays.copyOf(studentNames, 3);
        System.out.println("Top three      : " + Arrays.toString(topThree));

        // --- Arrays.copyOf: extended copy (extra slots become null) ---
        // Useful pattern for manually growing an array
        String[] expandedRoster = Arrays.copyOf(studentNames, 8);
        System.out.println("Expanded roster: " + Arrays.toString(expandedRoster));

        // --- Arrays.copyOfRange: extract a specific slice ---
        // Copies from index 1 (inclusive) to index 4 (exclusive) → Bob, Carol, David
        String[] middleStudents = Arrays.copyOfRange(studentNames, 1, 4);
        System.out.println("Middle students: " + Arrays.toString(middleStudents));

        // Verify independence: changing fullRoster does NOT affect studentNames
        fullRoster[0] = "Zara";
        System.out.println("\nAfter change:");
        System.out.println("studentNames[0]: " + studentNames[0]); // Still Alice
        System.out.println("fullRoster[0]  : " + fullRoster[0]);   // Now Zara
    }
}
▶ Output
Full roster : [Alice, Bob, Carol, David, Eve]
Top three : [Alice, Bob, Carol]
Expanded roster: [Alice, Bob, Carol, David, Eve, null, null, null]

Middle students: [Bob, Carol, David]

After change:
studentNames[0]: Alice
fullRoster[0] : Zara
🔥
Good to Know:`Arrays.copyOf` is internally implemented using `System.arraycopy`, so performance is essentially identical. Choose `Arrays.copyOf` for readability in everyday code, and reach for `System.arraycopy` only when you need fine-grained control over source/destination indices.

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.

ShallowVsDeepCopy.java · JAVA
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
import java.util.Arrays;

public class ShallowVsDeepCopy {

    // A simple mutable class representing a student
    static class Student {
        String name;
        int grade;

        Student(String name, int grade) {
            this.name = name;
            this.grade = grade;
        }

        @Override
        public String toString() {
            return name + "(" + grade + ")";
        }
    }

    public static void main(String[] args) {

        // --- PRIMITIVE ARRAY: shallow copy is fine ---
        int[] originalScores = {85, 90, 78};
        int[] scoreCopy = Arrays.copyOf(originalScores, originalScores.length);

        scoreCopy[0] = 999; // Does NOT affect originalScores
        System.out.println("Primitive original : " + Arrays.toString(originalScores));
        System.out.println("Primitive copy     : " + Arrays.toString(scoreCopy));

        // --- OBJECT ARRAY: shallow copy shares references ---
        Student[] classA = {
            new Student("Alice", 90),
            new Student("Bob",   85)
        };

        // Shallow copy — new array, but SAME Student objects inside
        Student[] classB = Arrays.copyOf(classA, classA.length);

        // Changing a FIELD on classB[0]'s Student also changes classA[0]'s Student!
        classB[0].grade = 55;

        System.out.println("\n--- Shallow Copy (Object Array) ---");
        System.out.println("classA after classB change: " + Arrays.toString(classA));
        System.out.println("classB                    : " + Arrays.toString(classB));
        System.out.println("Same Student object? " + (classA[0] == classB[0])); // true!

        // --- DEEP COPY: create new Student objects in a loop ---
        Student[] classC = {
            new Student("Carol", 92),
            new Student("David", 88)
        };

        Student[] classD = new Student[classC.length];
        for (int i = 0; i < classC.length; i++) {
            // Create a brand-new Student object with the same values
            classD[i] = new Student(classC[i].name, classC[i].grade);
        }

        // Now changing classD[0] does NOT affect classC[0]
        classD[0].grade = 10;

        System.out.println("\n--- Deep Copy (Object Array) ---");
        System.out.println("classC after classD change: " + Arrays.toString(classC));
        System.out.println("classD                    : " + Arrays.toString(classD));
        System.out.println("Same Student object? " + (classC[0] == classD[0])); // false!
    }
}
▶ Output
Primitive original : [85, 90, 78]
Primitive copy : [999, 90, 78]

--- Shallow Copy (Object Array) ---
classA after classB change: [Alice(55), Bob(85)]
classB : [Alice(55), Bob(85)]
Same Student object? true

--- Deep Copy (Object Array) ---
classC after classD change: [Carol(92), David(88)]
classD : [Carol(10), David(88)]
Same Student object? false
⚠️
Interview Gold:When an interviewer asks 'what's the difference between shallow and deep copy?', the answer they're fishing for is this: shallow copy duplicates the array structure but shares the objects inside. Deep copy duplicates both the structure and every object it contains. For primitive arrays, the distinction doesn't matter — but for object arrays it's critical.
Feature / AspectSystem.arraycopyArrays.copyOf / copyOfRangearray.clone()
Introduced inJava 1.0Java 6Java 1.0
Creates destination arrayNo — you create itYes — returned for youYes — returned for you
Copy a slice / partial rangeYes — full controlYes — via copyOfRangeNo — always full array
Insert into middle of targetYesNoNo
Resize while copyingNoYes — extend or truncateNo
PerformanceFastest (native)Same (uses arraycopy internally)Same (uses arraycopy internally)
ReadabilityLower — more parametersHigh — intention is clearHigh — very concise
Works on multidimensional arrays deeplyNo — shallow onlyNo — shallow onlyNo — shallow only
Best use casePerformance-critical slicingEveryday full or ranged copiesQuick 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.arraycopy for performance-critical or partial/offset copies; Arrays.copyOf / Arrays.copyOfRange for everyday readable copies; clone() for a quick one-liner on simple primitive arrays.

⚠ Common Mistakes to Avoid

  • Mistake 1: Using = to 'copy' an array — You write int[] backup = data; expecting a real copy, but you've only created an alias. Any change through backup silently mutates data. Fix: Always use Arrays.copyOf(data, data.length) or System.arraycopy to create a genuinely independent array.
  • Mistake 2: Assuming a shallow copy is safe for object arrays — You use Arrays.copyOf on a Student[] and confidently modify grades in the copy, only to discover the originals have changed too. Fix: For arrays of mutable objects, write a deep copy loop that constructs a fresh object for each element, or implement a copy constructor in your class.
  • Mistake 3: Getting the index bounds wrong in System.arraycopy — 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 satisfy sourceStart + length <= source.length. Double-check your arithmetic, or use Arrays.copyOfRange which handles bounds for you.

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?
  • QIf I write `String[] copyB = Arrays.copyOf(copyA, copyA.length)` and then do `copyB[0] = "New Name"`, does `copyA[0]` change? What if I do `copyB[0].toLowerCase()` — does that affect `copyA[0]`?
  • QWhen would you choose `System.arraycopy` over `Arrays.copyOf`, and is there any real performance difference between them for large arrays?

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.

🔥
TheCodeForge Editorial Team Verified Author

Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.

← PreviousJagged Arrays in JavaNext →Classes and Objects in Java
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged