Introduction
Imagine you're organizing a library. Sometimes you just want to reference where a book is (shallow copy), but other times you need to make an actual duplicate copy to put in another section (deep copy). Array copying in Java works similarly - sometimes you copy references, sometimes you duplicate actual data!
Array copying is fundamental in Java programming because arrays are objects, and simple assignment (array2 = array1) only copies references, not the actual data. Understanding different copying techniques is crucial for preventing bugs and managing memory efficiently.
What is Array Copying?
Array copying involves creating a new array with the same elements as the original array. However, the "depth" of the copy varies depending on the technique used.
Key Concepts:
- β Shallow Copy: Copies references to objects (shared objects)
- β Deep Copy: Creates new copies of objects (independent objects)
- β Primitive Arrays: Simple value copying
- β Object Arrays: Complex reference handling required
Code Explanation with Examples
Example 1: The Problem with Simple Assignment
public class AssignmentProblem {
public static void main(String[] args) {
// β THE PROBLEM: Simple assignment copies REFERENCES, not values
System.out.println("=== THE ASSIGNMENT PROBLEM ===");
int[] originalArray = {1, 2, 3, 4, 5};
int[] assignedArray = originalArray; // This copies the REFERENCE
System.out.println("Original array: " + java.util.Arrays.toString(originalArray));
System.out.println("Assigned array: " + java.util.Arrays.toString(assignedArray));
// Modify the assigned array
assignedArray[0] = 999;
System.out.println("\nAfter modifying assignedArray[0] = 999:");
System.out.println("Original array: " + java.util.Arrays.toString(originalArray));
System.out.println("Assigned array: " + java.util.Arrays.toString(assignedArray));
System.out.println("β οΈ Both arrays changed! This is usually NOT what we want!");
// π Demonstrating they are the same object
System.out.println("\n=== OBJECT IDENTITY ===");
System.out.println("originalArray == assignedArray: " + (originalArray == assignedArray));
System.out.println("originalArray hashCode: " + System.identityHashCode(originalArray));
System.out.println("assignedArray hashCode: " + System.identityHashCode(assignedArray));
// π― Object array example (even more problematic)
System.out.println("\n=== OBJECT ARRAY PROBLEM ===");
Person[] peopleOriginal = {
new Person("Alice", 25),
new Person("Bob", 30)
};
Person[] peopleAssigned = peopleOriginal; // Reference copy
peopleAssigned[0].setName("HACKED");
System.out.println("Original people[0]: " + peopleOriginal[0]);
System.out.println("Assigned people[0]: " + peopleAssigned[0]);
System.out.println("β οΈ Both arrays share the same Person objects!");
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
Output:
=== THE ASSIGNMENT PROBLEM ===
Original array: [1, 2, 3, 4, 5]
Assigned array: [1, 2, 3, 4, 5]
After modifying assignedArray[0] = 999:
Original array: [999, 2, 3, 4, 5]
Assigned array: [999, 2, 3, 4, 5]
β οΈ Both arrays changed! This is usually NOT what we want!
=== OBJECT IDENTITY ===
originalArray == assignedArray: true
originalArray hashCode: 1324119927
assignedArray hashCode: 1324119927
=== OBJECT ARRAY PROBLEM ===
Original people[0]: Person{name='HACKED', age=25}
Assigned people[0]: Person{name='HACKED', age=25}
β οΈ Both arrays share the same Person objects!
Example 2: Manual Looping Copy
public class ManualCopying {
public static void main(String[] args) {
System.out.println("=== MANUAL LOOP COPYING ===");
// π― Primitive array manual copy
int[] source = {10, 20, 30, 40, 50};
int[] destination = new int[source.length];
// Method 1: Standard for loop
for (int i = 0; i < source.length; i++) {
destination[i] = source[i];
}
System.out.println("Source: " + java.util.Arrays.toString(source));
System.out.println("Destination: " + java.util.Arrays.toString(destination));
// Modify destination to prove independence
destination[0] = 1000;
System.out.println("\nAfter modifying destination[0] = 1000:");
System.out.println("Source: " + java.util.Arrays.toString(source));
System.out.println("Destination: " + java.util.Arrays.toString(destination));
System.out.println("β
Arrays are independent!");
// π― Object array manual copy (SHALLOW COPY)
System.out.println("\n=== OBJECT ARRAY SHALLOW COPY ===");
Person[] peopleSource = {
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35)
};
Person[] peopleShallowCopy = new Person[peopleSource.length];
for (int i = 0; i < peopleSource.length; i++) {
peopleShallowCopy[i] = peopleSource[i]; // Copies references only!
}
System.out.println("Original people: " + java.util.Arrays.toString(peopleSource));
System.out.println("Shallow copy: " + java.util.Arrays.toString(peopleShallowCopy));
// Modify through shallow copy
peopleShallowCopy[0].setName("Modified Alice");
System.out.println("\nAfter modifying through shallow copy:");
System.out.println("Original people: " + java.util.Arrays.toString(peopleSource));
System.out.println("Shallow copy: " + java.util.Arrays.toString(peopleShallowCopy));
System.out.println("β οΈ Both arrays affected - it's a SHALLOW copy!");
// π― Object array DEEP COPY
System.out.println("\n=== OBJECT ARRAY DEEP COPY ===");
Person[] peopleDeepCopy = new Person[peopleSource.length];
for (int i = 0; i < peopleSource.length; i++) {
Person original = peopleSource[i];
// Create new Person objects with same data
peopleDeepCopy[i] = new Person(original.getName(), original.getAge());
}
System.out.println("Original people: " + java.util.Arrays.toString(peopleSource));
System.out.println("Deep copy: " + java.util.Arrays.toString(peopleDeepCopy));
// Modify through deep copy
peopleDeepCopy[1].setName("Modified Bob");
System.out.println("\nAfter modifying through deep copy:");
System.out.println("Original people: " + java.util.Arrays.toString(peopleSource));
System.out.println("Deep copy: " + java.util.Arrays.toString(peopleDeepCopy));
System.out.println("β
Arrays are independent - it's a DEEP copy!");
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
Output:
=== MANUAL LOOP COPYING ===
Source: [10, 20, 30, 40, 50]
Destination: [10, 20, 30, 40, 50]
After modifying destination[0] = 1000:
Source: [10, 20, 30, 40, 50]
Destination: [1000, 20, 30, 40, 50]
β
Arrays are independent!
=== OBJECT ARRAY SHALLOW COPY ===
Original people: [Person{name='Alice', age=25}, Person{name='Bob', age=30}, Person{name='Charlie', age=35}]
Shallow copy: [Person{name='Alice', age=25}, Person{name='Bob', age=30}, Person{name='Charlie', age=35}]
After modifying through shallow copy:
Original people: [Person{name='Modified Alice', age=25}, Person{name='Bob', age=30}, Person{name='Charlie', age=35}]
Shallow copy: [Person{name='Modified Alice', age=25}, Person{name='Bob', age=30}, Person{name='Charlie', age=35}]
β οΈ Both arrays affected - it's a SHALLOW copy!
=== OBJECT ARRAY DEEP COPY ===
Original people: [Person{name='Modified Alice', age=25}, Person{name='Bob', age=30}, Person{name='Charlie', age=35}]
Deep copy: [Person{name='Modified Alice', age=25}, Person{name='Bob', age=30}, Person{name='Charlie', age=35}]
After modifying through deep copy:
Original people: [Person{name='Modified Alice', age=25}, Person{name='Bob', age=30}, Person{name='Charlie', age=35}]
Deep copy: [Person{name='Modified Alice', age=25}, Person{name='Modified Bob', age=30}, Person{name='Charlie', age=35}]
β
Arrays are independent - it's a DEEP copy!
Example 3: System.arraycopy() Method
public class SystemArrayCopy {
public static void main(String[] args) {
System.out.println("=== System.arraycopy() METHOD ===");
// π― Basic primitive array copy
int[] source = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] destination = new int[10];
// Copy entire array
System.arraycopy(source, 0, destination, 0, source.length);
System.out.println("Full copy: " + java.util.Arrays.toString(destination));
// π― Partial copy
int[] partialCopy = new int[5];
System.arraycopy(source, 2, partialCopy, 0, 5); // Copy elements 2-6
System.out.println("Partial copy (elements 2-6): " + java.util.Arrays.toString(partialCopy));
// π― Overlapping copy
System.out.println("\n=== OVERLAPPING COPY ===");
int[] overlapping = {1, 2, 3, 4, 5};
System.out.println("Before overlapping copy: " + java.util.Arrays.toString(overlapping));
// Copy elements to the right (source and destination overlap)
System.arraycopy(overlapping, 0, overlapping, 1, 4);
System.out.println("After overlapping copy: " + java.util.Arrays.toString(overlapping));
// π― Object array copy (SHALLOW)
System.out.println("\n=== OBJECT ARRAY COPY (SHALLOW) ===");
String[] stringSource = {"Apple", "Banana", "Cherry", "Date"};
String[] stringDest = new String[stringSource.length];
System.arraycopy(stringSource, 0, stringDest, 0, stringSource.length);
System.out.println("String array copy: " + java.util.Arrays.toString(stringDest));
// Demonstrate it's shallow
stringDest[0] = "Apricot";
System.out.println("After modifying stringDest[0]:");
System.out.println("Source: " + java.util.Arrays.toString(stringSource));
System.out.println("Dest: " + java.util.Arrays.toString(stringDest));
System.out.println("β
String arrays are independent (Strings are immutable)");
// π― Performance comparison
System.out.println("\n=== PERFORMANCE COMPARISON ===");
int size = 1000000;
int[] largeArray = new int[size];
for (int i = 0; i < size; i++) {
largeArray[i] = i;
}
// Manual copy timing
long startTime = System.nanoTime();
int[] manualCopy = new int[size];
for (int i = 0; i < size; i++) {
manualCopy[i] = largeArray[i];
}
long manualTime = System.nanoTime() - startTime;
// System.arraycopy timing
startTime = System.nanoTime();
int[] systemCopy = new int[size];
System.arraycopy(largeArray, 0, systemCopy, 0, size);
long systemTime = System.nanoTime() - startTime;
System.out.println("Manual loop copy time: " + manualTime / 1000 + " microseconds");
System.out.println("System.arraycopy time: " + systemTime / 1000 + " microseconds");
System.out.println("System.arraycopy is " + (manualTime / systemTime) + "x faster!");
// π― Error cases
System.out.println("\n=== ERROR CASES ===");
try {
int[] smallArray = new int[3];
System.arraycopy(source, 0, smallArray, 0, source.length); // Too many elements
} catch (IndexOutOfBoundsException e) {
System.out.println("β IndexOutOfBoundsException: " + e.getMessage());
}
try {
System.arraycopy(null, 0, destination, 0, 5); // Null source
} catch (NullPointerException e) {
System.out.println("β NullPointerException: " + e.getMessage());
}
}
}
Output:
=== System.arraycopy() METHOD === Full copy: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Partial copy (elements 2-6): [3, 4, 5, 6, 7] === OVERLAPPING COPY === Before overlapping copy: [1, 2, 3, 4, 5] After overlapping copy: [1, 1, 2, 3, 4] === OBJECT ARRAY COPY (SHALLOW) === String array copy: [Apple, Banana, Cherry, Date] After modifying stringDest[0]: Source: [Apple, Banana, Cherry, Date] Dest: [Apricot, Banana, Cherry, Date] β String arrays are independent (Strings are immutable) === PERFORMANCE COMPARISON === Manual loop copy time: 4567 microseconds System.arraycopy time: 234 microseconds System.arraycopy is 19x faster! === ERROR CASES === β IndexOutOfBoundsException: arraycopy: length 10 exceeds destination array length 3 β NullPointerException: Cannot read the array length because "src" is null
Example 4: Arrays.copyOf() and Arrays.copyOfRange()
import java.util.Arrays;
public class ArraysCopyOfMethods {
public static void main(String[] args) {
System.out.println("=== Arrays.copyOf() METHODS ===");
// π― Arrays.copyOf() - exact copy or truncation/extension
int[] original = {1, 2, 3, 4, 5};
// Same length copy
int[] exactCopy = Arrays.copyOf(original, original.length);
System.out.println("Exact copy: " + Arrays.toString(exactCopy));
// Truncated copy
int[] truncated = Arrays.copyOf(original, 3);
System.out.println("Truncated to 3 elements: " + Arrays.toString(truncated));
// Extended copy (new elements get default values)
int[] extended = Arrays.copyOf(original, 8);
System.out.println("Extended to 8 elements: " + Arrays.toString(extended));
// π― Arrays.copyOfRange() - specific range copy
int[] rangeCopy = Arrays.copyOfRange(original, 1, 4); // from index 1 to 3 (inclusive, exclusive)
System.out.println("Range copy [1-4): " + Arrays.toString(rangeCopy));
// Extended range beyond original array
int[] extendedRange = Arrays.copyOfRange(original, 2, 8);
System.out.println("Extended range [2-8): " + Arrays.toString(extendedRange));
// π― Object array copying
System.out.println("\n=== OBJECT ARRAYS ===");
Person[] people = {
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35)
};
Person[] peopleCopy = Arrays.copyOf(people, people.length);
System.out.println("People copy: " + Arrays.toString(peopleCopy));
// Demonstrate shallow copy behavior
peopleCopy[0].setName("Modified Alice");
System.out.println("After modifying copy:");
System.out.println("Original: " + Arrays.toString(people));
System.out.println("Copy: " + Arrays.toString(peopleCopy));
System.out.println("β οΈ Both arrays affected - SHALLOW copy!");
// π― Multi-dimensional arrays
System.out.println("\n=== 2D ARRAYS ===");
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int[][] matrixShallowCopy = Arrays.copyOf(matrix, matrix.length);
System.out.println("Original matrix: " + Arrays.deepToString(matrix));
System.out.println("Shallow copy: " + Arrays.deepToString(matrixShallowCopy));
// Modify inner array
matrixShallowCopy[0][0] = 100;
System.out.println("After modifying matrixShallowCopy[0][0] = 100:");
System.out.println("Original: " + Arrays.deepToString(matrix));
System.out.println("Copy: " + Arrays.deepToString(matrixShallowCopy));
System.out.println("β οΈ Inner arrays are shared!");
// π― Deep copy for 2D arrays
int[][] matrixDeepCopy = new int[matrix.length][];
for (int i = 0; i < matrix.length; i++) {
matrixDeepCopy[i] = Arrays.copyOf(matrix[i], matrix[i].length);
}
matrixDeepCopy[1][1] = 500;
System.out.println("After deep copy modification:");
System.out.println("Original: " + Arrays.deepToString(matrix));
System.out.println("Deep copy: " + Arrays.deepToString(matrixDeepCopy));
System.out.println("β
Arrays are independent!");
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return name + "(" + age + ")";
}
}
Output:
=== Arrays.copyOf() METHODS === Exact copy: [1, 2, 3, 4, 5] Truncated to 3 elements: [1, 2, 3] Extended to 8 elements: [1, 2, 3, 4, 5, 0, 0, 0] Range copy [1-4): [2, 3, 4] Extended range [2-8): [3, 4, 5, 0, 0, 0] === OBJECT ARRAYS === People copy: [Alice(25), Bob(30), Charlie(35)] After modifying copy: Original: [Modified Alice(25), Bob(30), Charlie(35)] Copy: [Modified Alice(25), Bob(30), Charlie(35)] β οΈ Both arrays affected - SHALLOW copy! === 2D ARRAYS === Original matrix: [[1, 2, 3], [4, 5, 6], [7, 8, 9]] Shallow copy: [[1, 2, 3], [4, 5, 6], [7, 8, 9]] After modifying matrixShallowCopy[0][0] = 100: Original: [[100, 2, 3], [4, 5, 6], [7, 8, 9]] Copy: [[100, 2, 3], [4, 5, 6], [7, 8, 9]] β οΈ Inner arrays are shared! After deep copy modification: Original: [[100, 2, 3], [4, 5, 6], [7, 8, 9]] Deep copy: [[100, 2, 3], [4, 500, 6], [7, 8, 9]] β Arrays are independent!
Example 5: Clone() Method and Deep Copy Techniques
import java.util.Arrays;
public class CloneAndDeepCopy {
public static void main(String[] args) {
System.out.println("=== ARRAY.clone() METHOD ===");
// π― Primitive array clone
int[] original = {1, 2, 3, 4, 5};
int[] cloned = original.clone();
System.out.println("Original: " + Arrays.toString(original));
System.out.println("Cloned: " + Arrays.toString(cloned));
cloned[0] = 100;
System.out.println("After modifying clone:");
System.out.println("Original: " + Arrays.toString(original));
System.out.println("Cloned: " + Arrays.toString(cloned));
System.out.println("β
Primitive arrays are independent with clone()");
// π― Object array clone (SHALLOW)
System.out.println("\n=== OBJECT ARRAY CLONE (SHALLOW) ===");
Person[] people = {
new Person("Alice", 25),
new Person("Bob", 30)
};
Person[] peopleClone = people.clone();
System.out.println("Original: " + Arrays.toString(people));
System.out.println("Clone: " + Arrays.toString(peopleClone));
peopleClone[0].setName("Hacked Alice");
System.out.println("After modifying clone:");
System.out.println("Original: " + Arrays.toString(people));
System.out.println("Clone: " + Arrays.toString(peopleClone));
System.out.println("β οΈ Object arrays share references - SHALLOW copy!");
// π― Manual Deep Copy
System.out.println("\n=== MANUAL DEEP COPY ===");
Person[] peopleDeep = new Person[people.length];
for (int i = 0; i < people.length; i++) {
Person originalPerson = people[i];
peopleDeep[i] = new Person(originalPerson.getName(), originalPerson.getAge());
}
peopleDeep[1].setName("Deep Copy Bob");
System.out.println("After deep copy modification:");
System.out.println("Original: " + Arrays.toString(people));
System.out.println("Deep copy: " + Arrays.toString(peopleDeep));
System.out.println("β
True independence with deep copy!");
// π― Using Serialization for Deep Copy (complex objects)
System.out.println("\n=== SERIALIZATION DEEP COPY ===");
try {
Employee[] employees = {
new Employee("John", 50000, new Department("IT")),
new Employee("Jane", 60000, new Department("HR"))
};
Employee[] employeesDeep = deepCopy(employees);
employeesDeep[0].setName("Modified John");
employeesDeep[0].getDepartment().setName("Engineering");
System.out.println("Original: " + Arrays.toString(employees));
System.out.println("Deep copy: " + Arrays.toString(employeesDeep));
System.out.println("β
Complete deep copy achieved!");
} catch (Exception e) {
System.out.println("Serialization error: " + e.getMessage());
}
}
// Deep copy using serialization (requires Serializable)
@SuppressWarnings("unchecked")
public static <T> T[] deepCopy(T[] array) {
try {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
oos.writeObject(array);
oos.close();
java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(baos.toByteArray());
java.io.ObjectInputStream ois = new java.io.ObjectInputStream(bais);
return (T[]) ois.readObject();
} catch (Exception e) {
throw new RuntimeException("Deep copy failed", e);
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return name + "(" + age + ")";
}
}
// Serializable classes for deep copy demonstration
class Employee implements java.io.Serializable {
private String name;
private double salary;
private Department department;
public Employee(String name, double salary, Department department) {
this.name = name;
this.salary = salary;
this.department = department;
}
public String getName() { return name; }
public Department getDepartment() { return department; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return name + "($" + salary + ", " + department + ")";
}
}
class Department implements java.io.Serializable {
private String name;
public Department(String name) {
this.name = name;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return name;
}
}
Output:
=== ARRAY.clone() METHOD === Original: [1, 2, 3, 4, 5] Cloned: [1, 2, 3, 4, 5] After modifying clone: Original: [1, 2, 3, 4, 5] Cloned: [100, 2, 3, 4, 5] β Primitive arrays are independent with clone() === OBJECT ARRAY CLONE (SHALLOW) === Original: [Hacked Alice(25), Bob(30)] Clone: [Hacked Alice(25), Bob(30)] After modifying clone: Original: [Hacked Alice(25), Bob(30)] Clone: [Hacked Alice(25), Bob(30)] β οΈ Object arrays share references - SHALLOW copy! === MANUAL DEEP COPY === After deep copy modification: Original: [Hacked Alice(25), Bob(30)] Deep copy: [Hacked Alice(25), Deep Copy Bob(30)] β True independence with deep copy! === SERIALIZATION DEEP COPY === Original: [John($50000.0, IT), Jane($60000.0, HR)] Deep copy: [Modified John($50000.0, Engineering), Jane($60000.0, HR)] β Complete deep copy achieved!
Example 6: Real-World Use Cases and Best Practices
import java.util.Arrays;
public class RealWorldUseCases {
public static void main(String[] args) {
// π― USE CASE 1: Immutable data patterns
System.out.println("=== IMMUTABLE DATA PATTERN ===");
final int[] immutableData = {1, 2, 3, 4, 5};
// To "modify" immutable data, create a copy with changes
int[] updatedData = addElement(immutableData, 6);
System.out.println("Original: " + Arrays.toString(immutableData));
System.out.println("Updated: " + Arrays.toString(updatedData));
System.out.println("β
Original remains unchanged");
// π― USE CASE 2: Batch processing with sliding window
System.out.println("\n=== SLIDING WINDOW PROCESSING ===");
double[] sensorData = {10.5, 11.2, 12.8, 13.1, 14.6, 15.9, 16.2, 17.4};
int windowSize = 3;
for (int i = 0; i <= sensorData.length - windowSize; i++) {
double[] window = Arrays.copyOfRange(sensorData, i, i + windowSize);
double average = Arrays.stream(window).average().orElse(0.0);
System.out.printf("Window %d: %s β Average: %.2f%n",
i + 1, Arrays.toString(window), average);
}
// π― USE CASE 3: Data sanitization and filtering
System.out.println("\n=== DATA SANITIZATION ===");
Integer[] rawData = {1, 2, null, 4, null, 6, 7, null, 9};
System.out.println("Raw data: " + Arrays.toString(rawData));
// Remove null values by creating a new array
Integer[] cleanData = removeNulls(rawData);
System.out.println("Clean data: " + Arrays.toString(cleanData));
// π― USE CASE 4: Game state management
System.out.println("\n=== GAME STATE MANAGEMENT ===");
char[][] gameBoard = {
{'X', 'O', ' '},
{' ', 'X', 'O'},
{'O', ' ', 'X'}
};
// Save game state (deep copy for 2D array)
char[][] savedState = deepCopy2D(gameBoard);
System.out.println("Original board:");
printBoard(gameBoard);
// Make a move
gameBoard[0][2] = 'X';
System.out.println("After move:");
printBoard(gameBoard);
// Restore saved state
gameBoard = savedState;
System.out.println("Restored state:");
printBoard(gameBoard);
// π― BEST PRACTICES COMPARISON
System.out.println("\n=== COPYING TECHNIQUE CHOICE GUIDE ===");
System.out.println("Use Manual Loop When:");
System.out.println(" - You need custom logic during copying");
System.out.println(" - You're copying between different array types");
System.out.println(" - You need conditional copying");
System.out.println("\nUse System.arraycopy() When:");
System.out.println(" - You need maximum performance");
System.out.println(" - You're copying large arrays");
System.out.println(" - You need partial or overlapping copies");
System.out.println("\nUse Arrays.copyOf() When:");
System.out.println(" - You want simple, readable code");
System.out.println(" - You need to truncate or extend the array");
System.out.println(" - You're working with the entire array");
System.out.println("\nUse clone() When:");
System.out.println(" - You're copying primitive arrays");
System.out.println(" - You want concise syntax");
System.out.println(" - Performance is not critical");
}
// Immutable pattern: return new array with added element
public static int[] addElement(int[] array, int element) {
int[] newArray = Arrays.copyOf(array, array.length + 1);
newArray[newArray.length - 1] = element;
return newArray;
}
// Data sanitization: remove null values
public static Integer[] removeNulls(Integer[] array) {
// First, count non-null elements
int count = 0;
for (Integer element : array) {
if (element != null) count++;
}
// Create new array and copy non-null elements
Integer[] result = new Integer[count];
int index = 0;
for (Integer element : array) {
if (element != null) {
result[index++] = element;
}
}
return result;
}
// Deep copy for 2D char array
public static char[][] deepCopy2D(char[][] original) {
char[][] copy = new char[original.length][];
for (int i = 0; i < original.length; i++) {
copy[i] = Arrays.copyOf(original[i], original[i].length);
}
return copy;
}
public static void printBoard(char[][] board) {
for (char[] row : board) {
System.out.println(Arrays.toString(row));
}
}
}
Output:
=== IMMUTABLE DATA PATTERN === Original: [1, 2, 3, 4, 5] Updated: [1, 2, 3, 4, 5, 6] β Original remains unchanged === SLIDING WINDOW PROCESSING === Window 1: [10.5, 11.2, 12.8] β Average: 11.50 Window 2: [11.2, 12.8, 13.1] β Average: 12.37 Window 3: [12.8, 13.1, 14.6] β Average: 13.50 Window 4: [13.1, 14.6, 15.9] β Average: 14.53 Window 5: [14.6, 15.9, 16.2] β Average: 15.57 Window 6: [15.9, 16.2, 17.4] β Average: 16.50 === DATA SANITIZATION === Raw data: [1, 2, null, 4, null, 6, 7, null, 9] Clean data: [1, 2, 4, 6, 7, 9] === GAME STATE MANAGEMENT === Original board: [X, O, ] [ , X, O] [O, , X] After move: [X, O, X] [ , X, O] [O, , X] Restored state: [X, O, ] [ , X, O] [O, , X] === COPYING TECHNIQUE CHOICE GUIDE === Use Manual Loop When: - You need custom logic during copying - You're copying between different array types - You need conditional copying Use System.arraycopy() When: - You need maximum performance - You're copying large arrays - You need partial or overlapping copies Use Arrays.copyOf() When: - You want simple, readable code - You need to truncate or extend the array - You're working with the entire array Use clone() When: - You're copying primitive arrays - You want concise syntax - Performance is not critical
Copying Techniques Comparison Table
| Method | Type | Performance | Use Case | Deep Copy? |
|---|---|---|---|---|
| Manual Loop | Custom | Medium | Custom logic, conditional copying | With manual object creation |
| System.arraycopy() | Native | Very Fast | Large arrays, partial copies | No (shallow) |
| Arrays.copyOf() | Library | Fast | Simple full copies, resizing | No (shallow) |
| Arrays.copyOfRange() | Library | Fast | Partial range copies | No (shallow) |
| clone() | Native | Fast | Primitive arrays, concise syntax | No (shallow for objects) |
| Serialization | Custom | Slow | Complex object graphs | Yes |
Best Practices
- Choose the right tool: Use
System.arraycopy()for performance,Arrays.copyOf()for simplicity - Understand shallow vs deep: Most built-in methods create shallow copies
- Consider immutability: For truly immutable patterns, always return new copies
- Handle nulls gracefully: Check for null arrays before copying
- Validate array lengths: Ensure destination arrays are large enough
- Use deep copy for object graphs: When objects contain other objects
- Document copying behavior: Especially important in API design
Common Pitfalls
- Assuming deep copy: Most Java array methods create shallow copies
- Memory overhead: Deep copying can be expensive for large object graphs
- Circular references: Can cause infinite loops in manual deep copy implementations
- Concurrent modification: Copies can become stale if original changes
- Type safety: Raw arrays don't provide compile-time type checking
Conclusion
Array copying in Java is like photocopying documents:
- β Simple assignment = Sharing the original document
- β Shallow copy = Photocopying the document (pages are shared)
- β Deep copy = Retyping the entire document (completely independent)
Key Takeaways:
- Never use simple assignment (
array2 = array1) for copying data - Use
System.arraycopy()for maximum performance with large arrays - Use
Arrays.copyOf()for simple, readable full-array copies - Manual loops give you complete control for complex scenarios
- Always consider shallow vs deep copying semantics
- Test your copies to ensure they behave as expected
Mastering array copying techniques ensures your Java programs handle data correctly, efficiently, and predictably. Choose the right tool for each situation, and your arrays will always be in safe hands! πβ¨