Array Copy

Su Suriya Ravichandran Updated 14 Nov 2026
8 min read ·Lesson 68 of 133

Copying an array creates a new array containing the same elements as an existing one. It is needed whenever you want to change data without affecting the original, grow or shrink an array, extract part of it, or protect a class's internal array from outside modification. Java provides several ways to copy arrays, and the most important thing to understand is the difference between copying a reference and copying the contents.

Assignment Does Not Copy

int[] original = {1, 2, 3};
int[] other = original;       // copies the reference only

other[0] = 100;
System.out.println(original[0]);   // 100
original ──┐
           ├──► [100, 2, 3]
other    ──┘

Both variables point to the same array object, so a change through one is visible through the other. To get an independent array, you must create a new one and copy the elements into it.

Ways to Copy

MethodCreates a new array?Can change length?Typical use
arr.clone()YesNoSimple full copy
Arrays.copyOf(arr, newLength)YesYes (pads or truncates)Full copy, growing, shrinking
Arrays.copyOfRange(arr, from, to)YesYes (range length)Copying a slice
System.arraycopy(src, srcPos, dest, destPos, len)No (copies into an existing array)N/ACopying into a specific position, shifting elements
Manual loopYour choiceYour choiceCopying with transformation
Arrays.stream(arr).toArray()YesVia filteringCopy combined with filter/map

clone()

Every array has a public clone() method that returns a new array of the same type and length:

int[] original = {1, 2, 3};
int[] copy = original.clone();

copy[0] = 100;
System.out.println(Arrays.toString(original));   // [1, 2, 3]
System.out.println(Arrays.toString(copy));       // [100, 2, 3]

For arrays, clone() returns the correct array type directly, so no cast is needed.

Arrays.copyOf

Arrays.copyOf(original, newLength) copies into a new array of the given length:

int[] base = {10, 20, 30};

int[] same = Arrays.copyOf(base, 3);       // [10, 20, 30]
int[] longer = Arrays.copyOf(base, 5);     // [10, 20, 30, 0, 0]
int[] shorter = Arrays.copyOf(base, 2);    // [10, 20]

String[] names = {"Ada", "Linus"};
String[] padded = Arrays.copyOf(names, 4); // [Ada, Linus, null, null]

Extra slots get the type's default value (0, false, or null). This is the standard way to "resize" an array, which in Java always means creating a larger or smaller copy:

int[] data = new int[4];
// ... data is full ...
data = Arrays.copyOf(data, data.length * 2);   // the variable now refers to a bigger array

Arrays.copyOfRange

Copies a slice. The start index is inclusive and the end index is exclusive:

char[] letters = {'a', 'b', 'c', 'd', 'e', 'f'};

char[] middle = Arrays.copyOfRange(letters, 2, 5);   // [c, d, e]
char[] tail = Arrays.copyOfRange(letters, 4, letters.length);   // [e, f]

The length of the result is to - from. If to is beyond the end of the source, the extra positions are filled with defaults. If from is out of range or greater than to, an exception is thrown.

System.arraycopy

System.arraycopy copies elements into an existing destination array, at any position:

System.arraycopy(src, srcPos, dest, destPos, length);
ParameterMeaning
srcArray to copy from
srcPosStarting index in the source
destArray to copy into
destPosStarting index in the destination
lengthNumber of elements to copy
int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[8];

System.arraycopy(src, 1, dest, 3, 3);       // copy src[1..3] to dest[3..5]
System.out.println(Arrays.toString(dest));  // [0, 0, 0, 2, 3, 4, 0, 0]

It is implemented natively and is the fastest way to copy elements. Arrays.copyOf and ArrayList use it internally.

System.arraycopy correctly handles overlapping regions within the same array, which makes it ideal for shifting elements:

int[] a = {10, 20, 30, 40, 50};

// shift right by one from index 1, making room at index 1
System.arraycopy(a, 1, a, 2, 3);
a[1] = 15;
System.out.println(Arrays.toString(a));   // [10, 15, 20, 30, 40]

The method behaves as if the source range were first copied to a temporary buffer, so overlapping elements are not overwritten before they are read.

It throws:

  • IndexOutOfBoundsException if any range falls outside its array,
  • ArrayStoreException if the element types are incompatible (for example, copying an int[] into a double[], or a String into an Integer[]),
  • NullPointerException if either array is null.

Manual loop

A loop is the most flexible option, because it can transform each element while copying:

double[] pricesInr = {499, 1299, 89.5};
double[] pricesUsd = new double[pricesInr.length];
for (int i = 0; i < pricesInr.length; i++) {
    pricesUsd[i] = pricesInr[i] / 83.0;
}

For plain copies, prefer the built-in methods: they are shorter and faster.

Streams

When the copy should also filter or transform, a stream expresses it concisely:

int[] marks = {78, 42, 91, 35, 66};
int[] passing = Arrays.stream(marks).filter(m -> m >= 50).toArray();   // [78, 91, 66]
String[] upper = Arrays.stream(new String[]{"a", "b"})
        .map(String::toUpperCase)
        .toArray(String[]::new);                                       // [A, B]

toArray(String[]::new) tells the stream which array type to create.

Shallow Copy Versus Deep Copy

All the methods above make shallow copies. For arrays of primitives, that is all you need, since the values themselves are copied. For arrays of objects, only the references are copied; both arrays point to the same objects:

StringBuilder[] original = { new StringBuilder("draft") };
StringBuilder[] copy = original.clone();

copy[0].append(" v2");                       // modifies the shared object
System.out.println(original[0]);             // draft v2

copy[0] = new StringBuilder("replaced");     // replaces the reference in the copy only
System.out.println(original[0]);             // draft v2
original ──► [ ref ] ──┐
                       ├──► StringBuilder("draft v2")
copy     ──► [ ref ] ──┘

A deep copy creates new copies of the objects as well:

StringBuilder[] deep = new StringBuilder[original.length];
for (int i = 0; i < original.length; i++) {
    deep[i] = new StringBuilder(original[i]);   // a new object with the same content
}

Whether a deep copy is needed depends on whether the elements are mutable. Arrays of immutable objects (String, Integer, LocalDate, records with immutable components) can safely be copied shallowly, because the shared objects can never change.

The same applies to multidimensional arrays: int[][].clone() copies the outer array of row references, not the rows themselves. Deep-copy a 2D array by cloning each row.

Defensive Copying

Classes that store or return arrays must copy them to protect their own state. Otherwise, outside code holding a reference can change the class's data behind its back:

public class Exam {
    private final int[] scores;

    public Exam(int[] scores) {
        this.scores = scores.clone();        // copy on the way in
    }

    public int[] getScores() {
        return scores.clone();               // copy on the way out
    }

    public double average() {
        return Arrays.stream(scores).average().orElse(0);
    }
}
int[] input = {80, 90, 70};
Exam exam = new Exam(input);
input[0] = 0;                        // does not affect exam
exam.getScores()[1] = 0;             // modifies a copy, not exam's data
System.out.println(exam.average());  // 80.0

Without the two clone() calls, both changes would silently alter the Exam object.

Comparing Copies

After copying, use Arrays.equals (or Arrays.deepEquals for nested arrays) to compare contents. == reports whether two variables refer to the same array:

int[] a = {1, 2, 3};
int[] b = a.clone();
System.out.println(a == b);                // false: different arrays
System.out.println(Arrays.equals(a, b));   // true: same contents

Practical Example: A Growable Playlist

The following class stores songs in an array, grows it when full, inserts and removes items by shifting elements, and hands out safe snapshots. This is a simplified version of how ArrayList works internally.

import java.util.Arrays;

public class Playlist {
    private String[] songs = new String[2];
    private int size = 0;

    void add(String song) {
        if (size == songs.length) {
            songs = Arrays.copyOf(songs, songs.length * 2);
        }
        songs[size++] = song;
    }

    void insert(int index, String song) {
        if (index < 0 || index > size) throw new IndexOutOfBoundsException(index);
        if (size == songs.length) {
            songs = Arrays.copyOf(songs, songs.length * 2);
        }
        System.arraycopy(songs, index, songs, index + 1, size - index);
        songs[index] = song;
        size++;
    }

    String remove(int index) {
        if (index < 0 || index >= size) throw new IndexOutOfBoundsException(index);
        String removed = songs[index];
        System.arraycopy(songs, index + 1, songs, index, size - index - 1);
        songs[--size] = null;
        return removed;
    }

    String[] snapshot() {
        return Arrays.copyOf(songs, size);
    }

    public static void main(String[] args) {
        Playlist p = new Playlist();
        p.add("Intro");
        p.add("Chorus");
        p.add("Bridge");
        p.insert(1, "Verse");
        String[] before = p.snapshot();

        p.remove(0);
        p.add("Outro");

        System.out.println("Before:   " + Arrays.toString(before));
        System.out.println("After:    " + Arrays.toString(p.snapshot()));
        System.out.println("Capacity: " + p.songs.length + ", size: " + p.size);
    }
}

Output:

Before:   [Intro, Verse, Chorus, Bridge]
After:    [Verse, Chorus, Bridge, Outro]
Capacity: 4, size: 4

Each copying tool has a specific role:

  • Arrays.copyOf to grow. When the array is full, a new one twice the size is created and the old elements are copied in. The third add grows capacity from 2 to 4.
  • System.arraycopy to make room. insert shifts everything from index one place to the right within the same array; the overlapping copy is handled correctly.
  • System.arraycopy to close a gap. remove shifts the following elements left. It then sets the freed last slot to null, so the removed object can be garbage collected.
  • Arrays.copyOf(songs, size) for snapshots. It returns only the used part of the array as a new array. The before snapshot is unaffected by later changes, and callers can never modify the playlist's internal array.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Array Copy and get answers drawn from it.

Signed-in readers only.