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:
Here are the methods’ signatures:
Class | Method Definition |
Object | Object clone () throws CloneNotSupportedException |
System | static void arraycopy ( Object src, int srcPos, Object dest, int destPos, int length) |
Arrays | static boolean[] copyOf (T[] src, int newLength) static T[] copyOfRange (T[] src, int from, int to) |
Stream | Object[] 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
, andObject
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
andString
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
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
– anInstant
variable to store the time when the test starts.finish
– anInstant
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 viaArrays.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 viaStream.toArray
.via_Object_clone
– creates a copy of integer array viaObject.clone
method.via_System_arraycopy
– creates a copy of integer array viaSystem.arraycopy
.validate_IntArray
– validateclonedArray
has a different object reference fromsourceIntegerArray
. ValidateclonedArray
has the same value for each element at the same index. Validate changing eitherclonedArray
orsourceIntegerArray
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
– validateclonedArray
has a different object reference fromsourceStringArray
. ValidateclonedArray
has the same value for each element at the same index. Validate changing eitherclonedArray
orsourceStringArray
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 throwsNullPointerException
when the destination is a null object.exception_when_copy_length_exceed_destination
– it throwsArrayIndexOutOfBoundsException
when the destination size is smaller than the specified range.exception_when_copy_length_exceed_source
– it throwsArrayIndexOutOfBoundsException
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.
You can download the full source code of this example here: Java Copy Array Example
Last updated on Sept. 30, 2019
This is some of the most inefficient, long-winded code I have ever seen for accomplishing trivial tasks.