Make copy of an array

Learn make copy of an array with practical examples, diagrams, and best practices. Covers java, arrays, copy development techniques with visual explanations.

Mastering Array Copying in Java: Techniques and Best Practices

Hero image for Make copy of an array

Learn various methods to effectively copy arrays in Java, understanding their nuances, performance implications, and when to use each for shallow vs. deep copies.

Copying arrays is a fundamental operation in programming, especially in Java. While it might seem straightforward, there are several ways to achieve it, each with its own characteristics regarding performance, flexibility, and whether it performs a shallow or deep copy. Understanding these differences is crucial for writing robust and efficient Java applications. This article will explore the most common and effective methods for copying arrays, from built-in language features to utility class methods.

Shallow vs. Deep Copy: Understanding the Difference

Before diving into specific techniques, it's vital to grasp the distinction between shallow and deep copies, particularly when dealing with arrays of objects.

  • Shallow Copy: When you perform a shallow copy of an array, a new array object is created. However, the elements within this new array are references to the same objects as in the original array. If the array contains primitive types (like int, char, boolean), a shallow copy effectively copies the values. But for object types, changing an object through a reference in the copied array will also affect the original array, because both arrays point to the same underlying object.

  • Deep Copy: A deep copy, on the other hand, creates a completely independent copy of the array and all the objects it contains. This means new objects are created for each element, ensuring that modifications to the copied array's elements do not impact the original array's elements. Deep copying is more complex and often requires custom implementation, especially for complex object graphs.

flowchart LR
    subgraph Original Array
        A[Array Ref] --> B(Element 1 Ref)
        A --> C(Element 2 Ref)
    end

    subgraph Shallow Copy
        D[Array Ref Copy] --> B
        D --> C
    end

    subgraph Deep Copy
        E[Array Ref Copy] --> F(Element 1 Copy Ref)
        E --> G(Element 2 Copy Ref)
    end

    B -- "Points to" --> H[Object 1]
    C -- "Points to" --> I[Object 2]
    F -- "Points to" --> J[New Object 1]
    G -- "Points to" --> K[New Object 2]

    H -- "Different from" --- J
    I -- "Different from" --- K

    style A fill:#f9f,stroke:#333,stroke-width:2px
    style D fill:#ccf,stroke:#333,stroke-width:2px
    style E fill:#cfc,stroke:#333,stroke-width:2px
    style H fill:#fcc,stroke:#333,stroke-width:2px
    style I fill:#fcc,stroke:#333,stroke-width:2px
    style J fill:#cff,stroke:#333,stroke-width:2px
    style K fill:#cff,stroke:#333,stroke-width:2px

Visualizing Shallow vs. Deep Array Copies with Object References

Common Methods for Copying Arrays

Java provides several built-in mechanisms for copying arrays. Each method has its use cases and performance characteristics. We'll explore the most frequently used ones.

1. Using System.arraycopy()

The System.arraycopy() method is a native, low-level method provided by the Java platform. It's often the most efficient way to copy a contiguous block of data from one array to another. It performs a shallow copy.

public class ArrayCopyExample {
    public static void main(String[] args) {
        int[] originalArray = {1, 2, 3, 4, 5};
        int[] copiedArray = new int[originalArray.length];

        System.arraycopy(originalArray, 0, copiedArray, 0, originalArray.length);

        System.out.println("Original Array: " + java.util.Arrays.toString(originalArray));
        System.out.println("Copied Array:   " + java.util.Arrays.toString(copiedArray));

        // Modify copiedArray to show independence for primitives
        copiedArray[0] = 99;
        System.out.println("\nAfter modification:");
        System.out.println("Original Array: " + java.util.Arrays.toString(originalArray));
        System.out.println("Copied Array:   " + java.util.Arrays.toString(copiedArray));

        // Example with objects (shallow copy behavior)
        String[] originalStrings = {"apple", "banana", "cherry"};
        String[] copiedStrings = new String[originalStrings.length];
        System.arraycopy(originalStrings, 0, copiedStrings, 0, originalStrings.length);

        System.out.println("\nOriginal Strings: " + java.util.Arrays.toString(originalStrings));
        System.out.println("Copied Strings:   " + java.util.Arrays.toString(copiedStrings));

        // Modifying an element in copiedStrings (String is immutable, so it's effectively a deep copy for String elements)
        // If it were a mutable object, changes would reflect in originalStrings
        copiedStrings[0] = "apricot";
        System.out.println("\nAfter modification (Strings):");
        System.out.println("Original Strings: " + java.util.Arrays.toString(originalStrings));
        System.out.println("Copied Strings:   " + java.util.Arrays.toString(copiedStrings));
    }
}

Using System.arraycopy() for primitive and object arrays.

2. Using Arrays.copyOf() and Arrays.copyOfRange()

The java.util.Arrays class provides convenient static methods for copying arrays. These methods create a new array and copy elements into it. They also perform a shallow copy.

import java.util.Arrays;

public class ArraysCopyExample {
    public static void main(String[] args) {
        int[] originalArray = {10, 20, 30, 40, 50};

        // Using Arrays.copyOf() - copies the entire array
        int[] copiedArrayFull = Arrays.copyOf(originalArray, originalArray.length);
        System.out.println("Full Copied Array: " + Arrays.toString(copiedArrayFull));

        // Using Arrays.copyOfRange() - copies a specific range
        // Copies elements from index 1 (inclusive) to 4 (exclusive)
        int[] copiedArrayRange = Arrays.copyOfRange(originalArray, 1, 4);
        System.out.println("Range Copied Array: " + Arrays.toString(copiedArrayRange));

        // Example with objects (shallow copy behavior)
        StringBuilder[] originalBuilders = {new StringBuilder("A"), new StringBuilder("B")};
        StringBuilder[] copiedBuilders = Arrays.copyOf(originalBuilders, originalBuilders.length);

        System.out.println("\nOriginal Builders: " + Arrays.toString(originalBuilders));
        System.out.println("Copied Builders:   " + Arrays.toString(copiedBuilders));

        // Modifying an element in copiedBuilders affects originalBuilders
        copiedBuilders[0].append("ppend");
        System.out.println("\nAfter modification (Builders):");
        System.out.println("Original Builders: " + Arrays.toString(originalBuilders));
        System.out.println("Copied Builders:   " + Arrays.toString(copiedBuilders));
    }
}

Demonstrating Arrays.copyOf() and Arrays.copyOfRange().

3. Using the clone() Method

Arrays in Java implement the Cloneable interface, allowing you to use the clone() method to create a shallow copy. This method returns an Object, so you'll need to cast it back to the correct array type.

import java.util.Arrays;

public class ArrayCloneExample {
    public static void main(String[] args) {
        double[] originalArray = {1.1, 2.2, 3.3};

        // Using clone() method
        double[] copiedArray = originalArray.clone();

        System.out.println("Original Array: " + Arrays.toString(originalArray));
        System.out.println("Copied Array:   " + Arrays.toString(copiedArray));

        // Modify copiedArray
        copiedArray[0] = 99.9;
        System.out.println("\nAfter modification:");
        System.out.println("Original Array: " + Arrays.toString(originalArray));
        System.out.println("Copied Array:   " + Arrays.toString(copiedArray));

        // Example with objects (shallow copy behavior)
        Integer[] originalIntegers = {100, 200, 300};
        Integer[] copiedIntegers = originalIntegers.clone();

        System.out.println("\nOriginal Integers: " + Arrays.toString(originalIntegers));
        System.out.println("Copied Integers:   " + Arrays.toString(copiedIntegers));

        // Integer objects are immutable, so this effectively acts like a deep copy for the elements themselves
        // If it were a mutable object, changes would reflect in originalIntegers
        copiedIntegers[0] = 400;
        System.out.println("\nAfter modification (Integers):");
        System.out.println("Original Integers: " + Arrays.toString(originalIntegers));
        System.out.println("Copied Integers:   " + Arrays.toString(copiedIntegers));
    }
}

Copying arrays using the clone() method.

4. Manual Iteration (Looping)

For complete control, especially when performing a deep copy or applying transformations during the copy process, a simple loop is often the most flexible approach.

import java.util.Arrays;

public class ManualCopyExample {
    public static void main(String[] args) {
        String[] originalArray = {"alpha", "beta", "gamma"};
        String[] copiedArray = new String[originalArray.length];

        for (int i = 0; i < originalArray.length; i++) {
            copiedArray[i] = originalArray[i]; // Shallow copy
        }

        System.out.println("Original Array: " + Arrays.toString(originalArray));
        System.out.println("Copied Array:   " + Arrays.toString(copiedArray));

        // Example of deep copy for custom mutable objects
        class MyObject {
            String value;
            public MyObject(String value) { this.value = value; }
            public String toString() { return "MyObject(" + value + ")"; }
            public MyObject deepCopy() { return new MyObject(this.value); }
        }

        MyObject[] originalObjects = {new MyObject("One"), new MyObject("Two")};
        MyObject[] deepCopiedObjects = new MyObject[originalObjects.length];

        for (int i = 0; i < originalObjects.length; i++) {
            deepCopiedObjects[i] = originalObjects[i].deepCopy(); // Deep copy
        }

        System.out.println("\nOriginal Objects: " + Arrays.toString(originalObjects));
        System.out.println("Deep Copied Objects: " + Arrays.toString(deepCopiedObjects));

        // Modify deepCopiedObjects
        deepCopiedObjects[0].value = "Modified One";
        System.out.println("\nAfter modification (Deep Copy):");
        System.out.println("Original Objects: " + Arrays.toString(originalObjects));
        System.out.println("Deep Copied Objects: " + Arrays.toString(deepCopiedObjects));
    }
}

Manual array copying and a custom deep copy example.

Choosing the Right Method

The best method for copying an array depends on your specific needs:

  • For primitive arrays or shallow copies of object arrays: System.arraycopy(), Arrays.copyOf(), or clone() are generally preferred for their efficiency and conciseness.
  • For partial copies: Arrays.copyOfRange() is ideal.
  • For deep copies of object arrays: A manual loop, often combined with custom copy logic within your objects, is necessary. Serialization/deserialization can also be used for deep copies, but it's generally less performant and more complex for simple array copying.