Core Java

Java Copy Array Example

1. Introduction

Java array is an object which represents a data structure that contains elements of a similar data type. Array in java is index-based; the first element of the array is stored at the 0 index. Java has provided several ways to copy array over time:

  • System.arraycopy – provided since version 1.0. It copies an array from a source array to a destination array. It starts the copy from the source position to the target position with the specified length.
  • Object.clone() – provided since version 1.0. It creates and returns a copy of the object. If the object is an array, then the array is cloned into a new array with the same content.
  • Arrays.copyOf – provided since version 1.6. It copies the specified array, truncating or padding with false (if necessary) so the copy has the specified length.
  • Arrays.copyOfRange – provided since version 1.6. It copies the specified range of the specified array into a new.
  • Stream.toArray – provides since version 1.8. It returns an array containing the elements of this stream. It is a deep copy as the copied array has a different object reference from the source array’s element object.

You can watch the following video and learn how to use arrays in Java:

Java Array Example How to use Arrays in Java – Video

Here are the methods’ signatures:

ClassMethod Definition
ObjectObject clone () throws CloneNotSupportedException
Systemstatic void arraycopy (
Object src, int srcPos, Object dest, int destPos, int length)
Arraysstatic boolean[] copyOf (T[] src, int newLength)
static T[] copyOfRange (T[] src, int from, int to)
StreamObject[] toArray ()

Parameters Definition

  • src – the source array.
  • srcPos – starting position in the source array.
  • dest – the destination array.
  • destPos – starting position in the destination data.
  • length – the number of array elements.
  • newLength – the length of the copy to be returned.
  • from – the initial index of the range to be copied, inclusive.
  • to – the final index of the range to be copied, exclusive.

In this example, I will demonstrate these copy methods with the following items:

  • Copy an Integer, String, and Object array and compare the execution time and memory used.
  • Copy a two-dimensional (2D) Integer array and compare the execution time and memory used.
  • Copy an Integer and String array and compare the performance benchmark.

2. Technologies used

The example code in this article was built and run using:

  • Java 11
  • Maven 3.3.9
  • Eclipse Oxygen
  • JUnit 4.12
  • JMH 1.21
copy array java
copy array java

3. Maven Project

3.1 Dependency

Add Junit and JMH to the pom.xml.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.jcg.zheng</groupId>
	<artifactId>copyarray-benchmark</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<properties>
		<maven.compiler.target>11</maven.compiler.target>
		<maven.compiler.source>11</maven.compiler.source>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.openjdk.jmh</groupId>
			<artifactId>jmh-core</artifactId>
			<version>1.21</version>
		</dependency>
		<dependency>
			<groupId>org.openjdk.jmh</groupId>
			<artifactId>jmh-generator-annprocess</artifactId>
			<version>1.21</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
		</dependency>
	</dependencies>

	<build>

		<plugins>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>exec-maven-plugin</artifactId>
				<executions>
					<execution>
						<id>run-benchmarks</id>
						<phase>integration-test</phase>
						<goals>
							<goal>exec</goal>
						</goals>
						<configuration>
							<classpathScope>test</classpathScope>
							<executable>java</executable>
							<arguments>
								<argument>-classpath</argument>
								<classpath />
								<argument>org.openjdk.jmh.Main</argument>
								<argument>.*</argument>
							</arguments>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>

	</build>
</project>

3.2 DemoObject

In this step, I will create a DemoObject class which has data members, getters, setters, and constructors.

DemoObject.java

package org.jcg.zheng;

public class DemoObject {

    private char charValue;
    private String name;
    private int number;

    public DemoObject(final char charValue, final int number, final String name) {
        super();
        this.charValue = charValue;
        this.name = name;
        this.number = number;
    }

    public DemoObject(DemoObject dObj) {
        if (dObj != null) {
            this.name = dObj.getName();
            this.charValue = dObj.getCharValue();
            this.number = dObj.getNumber();
        }
    }

    public char getCharValue() {
        return charValue;
    }

    public String getName() {
        return name;
    }

    public int getNumber() {
        return number;
    }

    @Override
    public String toString() {
        return "DataObject [number=" + number + ", charValue=" + charValue + ", name=" + name + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + charValue;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + number;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        DemoObject other = (DemoObject) obj;
        if (charValue != other.charValue)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (number != other.number)
            return false;
        return true;
    }

    public void setCharValue(char charValue) {
        this.charValue = charValue;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setNumber(int number) {
        this.number = number;
    }

}

3.3 Copy Int Array Benchmark

JMH is a Java harness for building, running, and analyzing nano/micro/milli/macro benchmarks written in Java and other languages targeting the JVM.

In this step, I will create a CopyIntArrayBenchmark class which utilizes the JMH to show the performance benchmark when copying an integer array via System.arraycopy, Arrays.copyOf, Arrays.copyOfRange, Stream.toArray, and Object.clone.

Please note that EmptyArray is created as the comparing base and all the copying methods use the same source array data.

CopyIntArrayBenchmark.java

package org.jcg.zheng;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Fork(value = 1)
@Warmup(iterations = 2)
public class CopyIntArrayBenchmark {

 private int[] sourceIntegerArray = { 1, 2, 3, 4, 5 };

 @Benchmark
 public int[] Arrays_int_copyOfRange() {
 return Arrays.copyOfRange(sourceIntegerArray, 0, sourceIntegerArray.length);
 }

 @Benchmark
 public int[] Arrays_Stream_Int_toArray() {
 return Arrays.stream(sourceIntegerArray).toArray();
 }

 @Benchmark
 public int[] clone_IntArray() {
 return sourceIntegerArray.clone();
 }

 @Benchmark
 public int[] System_arraycopy() {
 int[] destination = new int[3];

 System.arraycopy(sourceIntegerArray, 0, destination, 0, 3);
 return destination;
 }

 @Benchmark
 public int[] EmptyArray() {
 return new int[0];
 }

 public static void main(String[] args) {
 Options opt = new OptionsBuilder().include(CopyIntArrayBenchmark.class.getSimpleName()).build();

 try {
 new Runner(opt).run();
 } catch (RunnerException e) {
 e.printStackTrace();
 }
 }

}

3.4 Copy String Array Benchmark

In this step, I will create a CopyStringArrayBenchmark class which utilizes the JMH to show the performance benchmark when copying an integer array via System.arraycopy, Arrays.copyOf, Arrays.copyOfRange, Stream.toArray, and Object.clone.

CopyStringArrayBenchmark.java

package org.jcg.zheng;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Fork(value = 1)
@Warmup(iterations = 2)
public class CopyStringArrayBenchmark {

 private String[] sourceStringArray = { "Mary", "Zheng", "Test"};

 @Benchmark
 public String[] Arrays_String_copyOfRange() {
 return Arrays.copyOfRange(sourceStringArray, 0, sourceStringArray.length);
 }

 @Benchmark
 public String[] Arrays_Stream_String_toArray() {
 return Arrays.stream(sourceStringArray).toArray(String[]::new);
 }

 @Benchmark
 public String[] clone_StringArray() {
 return sourceStringArray.clone();
 }

 @Benchmark
 public String[] System_arraycopy() {
 String[] destination = new String[3];

 System.arraycopy(sourceStringArray, 0, destination, 0, 3);
 return destination;
 }

 @Benchmark
 public String[] EmptyArray() {
 return new String[0];
 }

 public static void main(String[] args) {
 Options opt = new OptionsBuilder().include(CopyStringArrayBenchmark.class.getSimpleName()).build();

 try {
 new Runner(opt).run()
 } catch (RunnerException e) {
 e.printStackTrace();
 }
 }

}

4. JUnit Tests

In this step, I will create several Junit test classes to demonstrate array copy for an Integer, String, and Object array. The array copy for other primitive data types: boolean, char, byte, short, long, double, and float are similar to the int type provided in this example.

4.1 Test Base

In this step, I will create a TestBase class to hold the common test data and setup and cleanup methods to calculate the execution time and memory used.

  • copyStartAtZeroIndex – set the copy to start at the 0 index of the source.
  • start – an Instant variable to store the time when the test starts.
  • finish – an Instant variable to store the time when the test completes.
  • setup – a method annotated with @Before.
  • cleanup – a method marked with @After which outputs the execution time and memory used for the test method.

TestBase.java

package org.jcg.zheng;

import java.time.Duration;
import java.time.Instant;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestName;

public class TestBase {

	@Rule
	public TestName name = new TestName();
	
	protected int copyStartAtZeroIndex = 0;
	
	Instant start;
	Instant finish;

	@After
	public void cleanup() {
		finish = Instant.now();
		Runtime after = Runtime.getRuntime();
		long totalAllocatedMemeryInBytes = after.totalMemory() - after.freeMemory();
		long totalTimeInNs = Duration.between(start, finish).toNanos();
		System.out.printf("\t%s completed in %d ns, used memory %d B\n", name.getMethodName(), totalTimeInNs,
				totalAllocatedMemeryInBytes);
	}

	@Before
	public void setup() {
		start = Instant.now();
	}

}

4.2 Copy Int Array

In this step, I will copy an Integer array via System.arraycopy, Arrays.copyOf, Arrays.copyOfRange, Stream.toArray, and Object.clone methods. It will validate copied data has a different object reference for all these methods.

  • via_Arrays_copyOf – creates a copy of an integer array via Arrays.copyOf method and validates that the copied data is same as the source data.
  • via_Arrays_copyOf_big – creates a copy of an integer array whose size is larger than the source.
  • via_Arrays_copyOf_small – creates a copy of an integer array whose size is smaller than the source data.
  • via_Arrays_copyOfRange – creates a copy of an integer array and validates that the copied array’s element contains the same object reference.
  • via_Arrays_stream_toArray – creates a copy of integer array via Stream.toArray.
  • via_Object_clone – creates a copy of integer array via Object.clone method.
  • via_System_arraycopy – creates a copy of integer array via System.arraycopy.
  • validate_IntArray – validate clonedArray has a different object reference from sourceIntegerArray. Validate clonedArray has the same value for each element at the same index. Validate changing either clonedArray or sourceIntegerArray does not affect each other.

CopyIntArrayTest.java

package org.jcg.zheng;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

import java.util.Arrays;

import org.junit.Test;

public class CopyIntArrayTest extends TestBase {

 private int[] sourceIntegerArray = { 1, 2, 3, 4, 5 };

 private void validate_IntArray(int[] clonedArray) {
 // the copied object is a different reference
 assertFalse(System.identityHashCode(sourceIntegerArray) - System.identityHashCode(clonedArray) == 0);

 // for primitive data type, change the clonedArray element does not affect the
 // source array
 clonedArray[0] = 99;

 assertEquals(99, clonedArray[0]);
 assertEquals(1, sourceIntegerArray[0]);

 sourceIntegerArray[1] = 99;
 assertEquals(2, clonedArray[1]);
 }

 @Test
 public void via_Arrays_copyOf() {
 // since 1.6
 int[] copyofArr = Arrays.copyOf(sourceIntegerArray, sourceIntegerArray.length);

 assertEquals(sourceIntegerArray.length, copyofArr.length);
 validate_IntArray(copyofArr);
 }

 @Test
 public void via_Arrays_copyOf_big() {
 int[] copyofArr = Arrays.copyOf(sourceIntegerArray, sourceIntegerArray.length + 1);

 assertEquals(sourceIntegerArray.length + 1, copyofArr.length);
 assertEquals(0, copyofArr[sourceIntegerArray.length]);
 validate_IntArray(copyofArr);
 }

 @Test
 public void via_Arrays_copyOf_small() {
 int[] copyofArr = Arrays.copyOf(sourceIntegerArray, sourceIntegerArray.length - 1);

 assertEquals(sourceIntegerArray.length - 1, copyofArr.length);
 validate_IntArray(copyofArr);
 }

 @Test
 public void via_Arrays_copyOfRange() {
 // since 1.6
 int[] copyofArr = Arrays.copyOfRange(sourceIntegerArray, copyStartAtZeroIndex, sourceIntegerArray.length);

 assertEquals(sourceIntegerArray.length, copyofArr.length);
 assertEquals(sourceIntegerArray[0], copyofArr[0]);
 assertEquals(sourceIntegerArray[1], copyofArr[1]);
 validate_IntArray(copyofArr);
 }

 @Test
 public void via_Arrays_stream_toArray() {
 // since 1.8
 int[] copyofArr = Arrays.stream(sourceIntegerArray).toArray();

 assertEquals(sourceIntegerArray.length, copyofArr.length);
 assertEquals(1, sourceIntegerArray[0]);
 assertEquals(1, copyofArr[0]);
 validate_IntArray(copyofArr);
 }

 @Test
 public void via_Object_clone() {
 // since 1.0
 int[] clonedArray = sourceIntegerArray.clone();

 assertEquals(sourceIntegerArray.length, clonedArray.length);
 assertEquals(sourceIntegerArray[0], clonedArray[0]);
 assertEquals(sourceIntegerArray[1], clonedArray[1]);
 assertEquals(sourceIntegerArray[2], clonedArray[2]);
 validate_IntArray(clonedArray);
 }

 @Test
 public void via_System_arraycopy() {
 int length = 3; int[] destination = new int[length];

 // since 1.0
 System.arraycopy(sourceIntegerArray, copyStartAtZeroIndex, destination, copyStartAtZeroIndex, length);

 assertEquals(length, destination.length);
 assertEquals(sourceIntegerArray[copyStartAtZeroIndex], destination[0]);
 assertEquals(sourceIntegerArray[copyStartAtZeroIndex + 1], destination[1]);
 assertEquals(sourceIntegerArray[copyStartAtZeroIndex + 2], destination[2]);
 validate_IntArray(destination);
 }
}

4.3 Copy String Array

In this step, I will create several test methods to copy a String array via System.arraycopy, Arrays.copyOf, Arrays.copyOfRange, Stream.toArray, and Object.clone methods. We will create similar methods as you seen at CopyIntArrayTest and validate that all these methods created a copy array with a different String object with same value.

  • validate_StringArray – validate clonedArray has a different object reference from sourceStringArray. Validate clonedArray has the same value for each element at the same index. Validate changing either clonedArray or sourceStringArray does not affect each other.

CopyStringArrayTest.java

package org.jcg.zheng;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

import java.util.Arrays;

import org.junit.Test;

public class CopyStringArrayTest extends TestBase {

	private String[] sourceStringArray = { "Mary", "Zheng", "Test" };

	private void validate_StringArray(String[] clonedArray) {
		// the copied object is a different reference
		assertFalse(System.identityHashCode(sourceStringArray) - System.identityHashCode(clonedArray) == 0);

		// change the clonedArray element does not affect the source array
		clonedArray[0] = "Dummy";

		assertEquals("Dummy", clonedArray[0]);
		assertEquals("Mary", sourceStringArray[0]);

		sourceStringArray[1] = "ZhengUpdate";
		assertEquals("Zheng", clonedArray[1]);
	}

	@Test
	public void via_Arrays_copyOf() {
		// since Java 1.6
		String[] copyofArr = Arrays.copyOf(sourceStringArray, sourceStringArray.length - 1);

		assertEquals(sourceStringArray.length - 1, copyofArr.length);
		assertEquals(sourceStringArray[0], copyofArr[0]);
		assertEquals(sourceStringArray[1], copyofArr[1]);

		validate_StringArray(copyofArr);
	}

	@Test
	public void via_Arrays_copyOfRange() {
		// since Java 1.6
		String[] copyofArr = Arrays.copyOfRange(sourceStringArray, copyStartAtZeroIndex, sourceStringArray.length - 1);

		assertEquals(sourceStringArray.length - 1, copyofArr.length);
		assertEquals(sourceStringArray[0], copyofArr[0]);
		assertEquals(sourceStringArray[1], copyofArr[1]);

		validate_StringArray(copyofArr);
	}

	@Test
	public void via_Arrays_stream_toArray() {
		String[] copyofArr = Arrays.stream(sourceStringArray).toArray(String[]::new);

		assertEquals(sourceStringArray.length, copyofArr.length);
		assertEquals(sourceStringArray[0], copyofArr[0]);
		assertEquals(sourceStringArray[1], copyofArr[1]);

		validate_StringArray(copyofArr);
	}

	@Test
	public void via_Object_clone() {
		// since 1.0
		String[] clonedArray = sourceStringArray.clone();

		assertEquals(sourceStringArray.length, clonedArray.length);
		assertEquals(sourceStringArray[0], clonedArray[0]);
		assertEquals(sourceStringArray[1], clonedArray[1]);

		validate_StringArray(clonedArray);
	}

	@Test
	public void via_System_arraycopy() {
		int length = 3;
		String[] destination = new String[length];

		// since 1.0
		System.arraycopy(sourceStringArray, copyStartAtZeroIndex, destination, copyStartAtZeroIndex, length);

		assertEquals(sourceStringArray.length, destination.length);
		assertEquals(sourceStringArray[0], destination[0]);
		assertEquals(sourceStringArray[1], destination[1]);

		validate_StringArray(destination);
	}
}

4.4 Copy Object Array

In this step, I will create several test methods to copy an object array via System.arraycopy, Arrays.copyOf, Arrays.copyOfRange, Stream.toArray, and Object.clone methods. Stream.toArray is a deep copy when copying an object array. The other four methods are a “shallow copy” when copying an object array.

  • validate_shallowCopy_ObjectArray – during shallow copy, the copied array’s elements hold the same object reference to the source array’s elements, so changing the array’s elements on either copy or source affects each other.
  • validate_deepCopy_ObjectArray – during deep copy, the copied array’s elements have a different object reference from the source array’s elements, so changing the copied element doesn’t affect the source’s element, and vice versa.

CopyObjectArrayTest.java

package org.jcg.zheng;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;

import org.junit.Test;

public class CopyObjectArrayTest extends TestBase {

	private int length = 3;

	private DemoObject maryObject = new DemoObject('A', 1, "Mary");
	private DemoObject someObject = new DemoObject('B', 2, "Some data for test");

	private DemoObject[] sourceObjectArray = { maryObject, someObject, null };

	private void validate_deepCopy_ObjectArray(DemoObject[] clonedArray) {
		// the copied object is a different reference
		assertFalse(System.identityHashCode(sourceObjectArray) - System.identityHashCode(clonedArray) == 0);

		assertEquals(3, clonedArray.length);

		// the object element reference is different
		assertFalse(System.identityHashCode(maryObject) - System.identityHashCode(clonedArray[0]) == 0);
		assertFalse(System.identityHashCode(someObject) - System.identityHashCode(clonedArray[1]) == 0);

		assertEquals(maryObject, clonedArray[0]);
		assertEquals(someObject, clonedArray[1]);

		// the object change will NOT affect the other because of deep copy
		clonedArray[1].setName("Changed");
		assertEquals("Some data for test", sourceObjectArray[1].getName());

		sourceObjectArray[1].setNumber(99);
		assertEquals(2, clonedArray[1].getNumber());
	}

	private void validate_shallowCopy_ObjectArray(DemoObject[] clonedArray) {
		// the copied object is a different reference
		assertFalse(System.identityHashCode(sourceObjectArray) - System.identityHashCode(clonedArray) == 0);

		// the copied object element is the same reference
		assertTrue(System.identityHashCode(maryObject) - System.identityHashCode(clonedArray[0]) == 0);
		assertTrue(System.identityHashCode(someObject) - System.identityHashCode(clonedArray[1]) == 0);

		assertEquals(maryObject, clonedArray[0]);
		assertEquals(someObject, clonedArray[1]);

		// for the copied object, change one will affect the other due to object
		// reference is same
		clonedArray[1].setName("Changed");
		assertEquals("Changed", sourceObjectArray[1].getName());

		sourceObjectArray[1].setNumber(99);
		assertEquals(99, clonedArray[1].getNumber());

	}

	@Test
	public void via_Arrays_copyof() {
		DemoObject[] clonedArray = Arrays.copyOf(sourceObjectArray, sourceObjectArray.length);

		assertEquals(sourceObjectArray.length, clonedArray.length);
		validate_shallowCopy_ObjectArray(clonedArray);
	}

	@Test
	public void via_Arrays_copyOfRange() {
		// since Java 1.6
		DemoObject[] clonedArray = Arrays.copyOfRange(sourceObjectArray, copyStartAtZeroIndex,
				sourceObjectArray.length - 1);

		assertEquals(sourceObjectArray.length - 1, clonedArray.length);
		validate_shallowCopy_ObjectArray(clonedArray);
	}

	@Test
	public void via_Arrays_copyOfRange_padding_null_when_dest_is_larger_then_original() {
		// since Java 1.6
		DemoObject[] clonedArray = Arrays.copyOfRange(sourceObjectArray, copyStartAtZeroIndex,
				sourceObjectArray.length + 1);

		assertEquals(sourceObjectArray.length + 1, clonedArray.length);
		assertNull(clonedArray[sourceObjectArray.length]);
		validate_shallowCopy_ObjectArray(clonedArray);
	}

	@Test
	public void via_Arrays_stream_toArray() {
		DemoObject[] clonedArray = Arrays.stream(sourceObjectArray).map(DemoObject::new).toArray(DemoObject[]::new);

		validate_deepCopy_ObjectArray(clonedArray);
	}

	@Test
	public void via_Object_clone() {
		// Since Java 1.0
		DemoObject[] clonedArray = sourceObjectArray.clone();

		assertEquals(sourceObjectArray.length, clonedArray.length);
		validate_shallowCopy_ObjectArray(clonedArray);
	}

	@Test
	public void via_System_arraycopy() {

		DemoObject[] destination = new DemoObject[length];
		// Since Java 1.0
		System.arraycopy(sourceObjectArray, copyStartAtZeroIndex, destination, copyStartAtZeroIndex, length);

		assertEquals(length, destination.length);
		validate_shallowCopy_ObjectArray(destination);
	}

	@Test
	public void via_System_arraycopy_padding_null_when_destination_is_bigger_than_original() {
		DemoObject[] destination = new DemoObject[5];

		// Since Java 1.0
		System.arraycopy(sourceObjectArray, copyStartAtZeroIndex, destination, copyStartAtZeroIndex,
				sourceObjectArray.length);

		assertNull(destination[sourceObjectArray.length]);
		assertNull(destination[sourceObjectArray.length + 1]);
	}

}

4.5 Copy 2D Int Array

In this step, I will create test methods to copy a 2D integer array via System.arraycopy, Arrays.copyOf, Arrays.copyOfRange, Stream.toArray, and Object.clone methods.

You will see at step 5.1, Stream.toArray writes less code, but it takes longer time and more memory to execute.

Copy2DIntArrayTest.java

package org.jcg.zheng;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;

import org.junit.Test;

public class Copy2DIntArrayTest extends TestBase {

	private int[][] clonedArrays;
	private int[][] source2DArray = { { 1, 2, 3, 4, 5 }, { 3, 4 } };

	protected void validate() {
		assertEquals(source2DArray.length, clonedArrays.length);
		assertEquals(source2DArray[0][0], clonedArrays[0][0]);
		assertEquals(source2DArray[0][1], clonedArrays[0][1]);
		assertEquals(source2DArray[1][0], clonedArrays[1][0]);
		assertEquals(source2DArray[1][1], clonedArrays[1][1]);
	}

	@Test
	public void via_Arrays_stream_toArray_lambda() {
		clonedArrays = Arrays.stream(source2DArray).map((int[] row) -> row.clone())
				.toArray((int length) -> new int[length][]);

		validate();
	}

	@Test
	public void via_Arrays_stream_toArray_methodReference() {
		clonedArrays = Arrays.stream(source2DArray).map(int[]::clone).toArray(int[][]::new);
		validate();
	 
	}

	@Test
	public void via_Object_clone() {
		clonedArrays = source2DArray.clone();
		validate();
	}

	@Test
	public void via_System_arraycopy() {
		clonedArrays = new int[source2DArray.length][];
		for (int i = 0; i < source2DArray.length; i++) {
			int[] aSource = source2DArray[i];
			clonedArrays[i] = new int[source2DArray.length];
			System.arraycopy(aSource, 0, clonedArrays[i], 0, source2DArray.length);
		}

		validate();
	}
	
	@Test
	public void via_Arrays_copyOf() {
		clonedArrays = new int[source2DArray.length][];
		for (int i = 0; i < source2DArray.length; i++) {
			int[] aSource = source2DArray[i];	
			clonedArrays[i] = Arrays.copyOf(aSource, aSource.length);
		}

		validate();
	}

}

4.6 System arraycopy

System.arraycopy will throw an exception when copying with invalid size. in this step, I will create a Junit test class to demonstrate these use cases.

  • exception_when_destination_is_null – it throws NullPointerException when the destination is a null object.
  • exception_when_copy_length_exceed_destination – it throws ArrayIndexOutOfBoundsException when the destination size is smaller than the specified range.
  • exception_when_copy_length_exceed_source – it throws ArrayIndexOutOfBoundsException when the copy range exceeds the original array index boundary.

System_arraycopyTest.java

package org.jcg.zheng;

import org.junit.Test;

public class System_arraycopyTest extends TestBase {

	private DemoObject maryObject = new DemoObject('A', 1, "Mary");
	private DemoObject someObject = new DemoObject('B', 2, "Some data for test");

	private DemoObject[] sourceObjectArray = { maryObject, someObject, null };

	@Test(expected = ArrayIndexOutOfBoundsException.class)
	public void exception_when_copy_lenght_exceed_destination() {
		DemoObject[] destination = new DemoObject[1];
		// Since Java 1.0
		System.arraycopy(sourceObjectArray, 0, destination, 0, destination.length + 1);
	}

	@Test(expected = ArrayIndexOutOfBoundsException.class)
	public void exception_when_copy_length_exceed_source() {
		DemoObject[] destination = new DemoObject[5];
		// Since Java 1.0
		System.arraycopy(sourceObjectArray, 0, destination, 0, sourceObjectArray.length + 1);
	}

	@Test(expected = NullPointerException.class)
	public void exception_when_destination_is_null() {
		System.arraycopy(sourceObjectArray, 0, null, 0, sourceObjectArray.length);
	}

}

5. Demo

5.1 Junit Test Reports

I will execute the Junit tests and capture the results.

Junit Output

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.jcg.zheng.Copy2DIntArrayTest
        via_Object_clone completed in 0 ns, used memory 5538560 B
        via_Arrays_stream_toArray_lambda completed in 17498500 ns, used memory 6127328 B
        via_Arrays_stream_toArray_methodReference completed in 999700 ns, used memory 6291456 B
        via_System_arraycopy completed in 0 ns, used memory 6586512 B
        via_Arrays_copyOf completed in 0 ns, used memory 6978464 B
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.248 sec
Running org.jcg.zheng.CopyIntArrayTest
        via_Object_clone completed in 0 ns, used memory 7340032 B
        via_Arrays_copyOfRange completed in 0 ns, used memory 7684264 B
        via_Arrays_copyOf_small completed in 0 ns, used memory 8076216 B
        via_Arrays_stream_toArray completed in 3999500 ns, used memory 8420448 B
        via_Arrays_copyOf_big completed in 0 ns, used memory 8764680 B
        via_System_arraycopy completed in 500800 ns, used memory 9108912 B
        via_Arrays_copyOf completed in 500100 ns, used memory 9469024 B
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.025 sec
Running org.jcg.zheng.CopyObjectArrayTest
        via_Arrays_copyOfRange_padding_null_when_dest_is_larger_then_original completed in 0 ns, used memory 9878312 B
        via_Object_clone completed in 500400 ns, used memory 10270264 B
        via_System_arraycopy_padding_null_when_destination_is_bigger_than_original completed in 0 ns, used memory 10614496 B
        via_Arrays_copyOfRange completed in 999600 ns, used memory 10958728 B
        via_Arrays_stream_toArray completed in 1998500 ns, used memory 11350680 B
        via_System_arraycopy completed in 0 ns, used memory 11712248 B
        via_Arrays_copyof completed in 0 ns, used memory 12056480 B
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.01 sec
Running org.jcg.zheng.CopyStringArrayTest
        via_Object_clone completed in 0 ns, used memory 12448432 B
        via_Arrays_copyOfRange completed in 0 ns, used memory 13054424 B
        via_Arrays_stream_toArray completed in 4000700 ns, used memory 13201952 B
        via_System_arraycopy completed in 0 ns, used memory 13546184 B
        via_Arrays_copyOf completed in 0 ns, used memory 13907752 B
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.021 sec
Running org.jcg.zheng.System_arraycopyTest
        exception_when_destination_is_null completed in 0 ns, used memory 14564408 B
        exception_when_copy_length_exceed_source completed in 500400 ns, used memory 14662760 B
        exception_when_copy_lenght_exceed_destination completed in 498800 ns, used memory 1963368 B
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.006 sec

Results :

Tests run: 27, Failures: 0, Errors: 0, Skipped: 0

Note:

  • When copying a 2D integer array, Object.clone method is the fastest and consumes the least memory. Please refer to line 05-09.
  • When copying an integer array, Object.clone method is the fastest and consumes the least memory. Please refer to line 12-18.
  • When copying an object array, Array.copyofRange method is the fastest and consumers the least memory. Please refer to line 21-27.
  • When copying a String array, Object.clone method is the fastest and consumes the least memory. Please refer to line 30-34.

5.2 JMH Performance Benchmark Reports

I will execute the two benchmark classes and capture the output here.

JMH Benchmark Output

Result "org.jcg.zheng.CopyIntArrayBenchmark.clone_IntArray":
  19.686 ±(99.9%) 9.409 ns/op [Average]
  (min, avg, max) = (18.463, 19.686, 24.053), stdev = 2.444
  CI (99.9%): [10.276, 29.095] (assumes normal distribution)


# Run complete. Total time: 00:05:56

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                        Mode  Cnt   Score    Error  Units
CopyIntArrayBenchmark.Arrays_Stream_Int_toArray  avgt    5  94.195 ± 25.917  ns/op
CopyIntArrayBenchmark.Arrays_int_copyOfRange     avgt    5  28.460 ± 18.121  ns/op
CopyIntArrayBenchmark.EmptyArray                 avgt    5  12.399 ±  9.217  ns/op
CopyIntArrayBenchmark.System_arraycopy           avgt    5  14.454 ±  7.854  ns/op
CopyIntArrayBenchmark.clone_IntArray             avgt    5  19.686 ±  9.409  ns/op


Result "org.jcg.zheng.CopyStringArray.clone_StringArray":
  16.729 ±(99.9%) 1.157 ns/op [Average]
  (min, avg, max) = (16.305, 16.729, 17.021), stdev = 0.300
  CI (99.9%): [15.572, 17.886] (assumes normal distribution)


# Run complete. Total time: 00:05:55

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                     Mode  Cnt   Score    Error  Units
CopyStringArray.Arrays_Stream_String_toArray  avgt    5  84.724 ± 55.931  ns/op
CopyStringArray.Arrays_String_copyOfRange     avgt    5  16.286 ±  2.151  ns/op
CopyStringArray.EmptyArray                    avgt    5   7.368 ±  1.201  ns/op
CopyStringArray.System_arraycopy              avgt    5  10.444 ±  1.720  ns/op
CopyStringArray.clone_StringArray             avgt    5  16.729 ±  1.157  ns/op

Note: System.arraycopy is the best method under the performance testing.

6. Java Copy Array – Summary

In this example, we demonstrated how to copy an integer array with five methods: System.arraycopy, Object.clone, Arrays.copyOf, Arrays.copyOfRange, and Stream.toArray.

We demonstrated that Stream.toArray is a deep copy when copying an object array. The other four methods are a “shallow copy” when copying an object array.

We compared the execution time and memory used for these methods as well as the performance benchmark. We found out that System.arraycopy has the least amount of average execution time with benchmark tests and Object.clone is the fastest method using the least amount of memory under unit tests.

7. Download the Source Code

This example consists of a Maven project to copy array java.

Download
You can download the full source code of this example here: Java Copy Array Example

Last updated on Sept. 30, 2019

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Norman Mark
Norman Mark
3 years ago

This is some of the most inefficient, long-winded code I have ever seen for accomplishing trivial tasks.

Back to top button