Clone Method for Java Arrays

Clone method for Java arrays

When the clone method is invoked upon an array, it returns a reference to a new array which contains (or references) the same elements as the source array.

So in your example, int[] a is a separate object instance created on the heap and int[] b is a separate object instance created on the heap. (Remember all arrays are objects).

    int[] a = {1,2,3};
int[] b = a.clone();

System.out.println(a == b ? "Same Instance":"Different Instance");
//Outputs different instance

If were to modify int[] b the changes would not be reflected on int[] a since the two are separate object instances.

    b[0] = 5;
System.out.println(a[0]);
System.out.println(b[0]);
//Outputs: 1
// 5

This becomes slightly more complicated when the source array contains objects. The clone method will return a reference to a new array, which references the same objects as the source array.

So if we have the class Dog...

    class Dog{

private String name;

public Dog(String name) {
super();
this.name = name;
}

public String getName() {
return name;
}

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

}

and I create and populate an array of type Dog...

    Dog[] myDogs = new Dog[4];

myDogs[0] = new Dog("Wolf");
myDogs[1] = new Dog("Pepper");
myDogs[2] = new Dog("Bullet");
myDogs[3] = new Dog("Sadie");

then clone dog...

    Dog[] myDogsClone = myDogs.clone();

the arrays refer to the same elements...

    System.out.println(myDogs[0] == myDogsClone[0] ? "Same":"Different");
System.out.println(myDogs[1] == myDogsClone[1] ? "Same":"Different");
System.out.println(myDogs[2] == myDogsClone[2] ? "Same":"Different");
System.out.println(myDogs[3] == myDogsClone[3] ? "Same":"Different");
//Outputs Same (4 Times)

This means if we modify an object accessed through the cloned array, the changes will be reflected when we access the same object in the source array, since they point to the same reference.

    myDogsClone[0].setName("Ruff"); 
System.out.println(myDogs[0].getName());
//Outputs Ruff

However, changes to the array itself will only affect that array.

    myDogsClone[1] = new Dog("Spot");
System.out.println(myDogsClone[1].getName());
System.out.println(myDogs[1].getName());
//Outputs Spot
// Pepper

If you generally understand how object references work, it is easy to understand how arrays of objects are impacted by cloning and modifications. To gain further insight into references and primitives I would suggest reading this excellent article.

Gist of Source Code

Why clone() is the best way for copying arrays?

I would like to make some points about why clone() is the fastest way to copy an array than System.arraycopy(..) or others:

1. clone() doesn't have to do the typechecking before copying a source array to the destination one as provided here. It just simple allocates new memory space and assigns the objects to it. On the other hand, System.arraycopy(..) checks for the type and then copies an array.

2. clone() also breaks the optimization to eliminate redundant zeroing. As you know, every allocated array in Java must be initialized with 0s or respective default values. However, JIT can avoid zeroing this array if it sees that the array is filled right after creation. That makes it definitely faster compared to changing the copy values with existing 0s or respective default values. While using System.arraycopy(..) spends significant amount of time clearing and copying the initialized array. To do so I have performed some of the benchmark tests.

@BenchmarkMode(Mode.Throughput)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 10, time = 1, batchSize = 1000)
@Measurement(iterations = 10, time = 1, batchSize = 1000)
public class BenchmarkTests {

@Param({"1000","100","10","5", "1"})
private int size;
private int[] original;

@Setup
public void setup() {
original = new int[size];
for (int i = 0; i < size; i++) {
original[i] = i;
}
}

@Benchmark
public int[] SystemArrayCopy() {
final int length = size;
int[] destination = new int[length];
System.arraycopy(original, 0, destination, 0, length);
return destination;
}

@Benchmark
public int[] arrayClone() {
return original.clone();
}

}

Output:

Benchmark                        (size)   Mode  Cnt       Score      Error  Units
ArrayCopy.SystemArrayCopy 1 thrpt 10 26324.251 ± 1532.265 ops/s
ArrayCopy.SystemArrayCopy 5 thrpt 10 26435.562 ± 2537.114 ops/s
ArrayCopy.SystemArrayCopy 10 thrpt 10 27262.200 ± 2145.334 ops/s
ArrayCopy.SystemArrayCopy 100 thrpt 10 10524.117 ± 474.325 ops/s
ArrayCopy.SystemArrayCopy 1000 thrpt 10 984.213 ± 121.934 ops/s
ArrayCopy.arrayClone 1 thrpt 10 55832.672 ± 4521.112 ops/s
ArrayCopy.arrayClone 5 thrpt 10 48174.496 ± 2728.928 ops/s
ArrayCopy.arrayClone 10 thrpt 10 46267.482 ± 4641.747 ops/s
ArrayCopy.arrayClone 100 thrpt 10 19837.480 ± 364.156 ops/s
ArrayCopy.arrayClone 1000 thrpt 10 1841.145 ± 110.322 ops/s

According to the outputs I am getting that clone is almost twice fast from System.arraycopy(..)

3. Also, using manual copying method like clone() results into faster ouput because it doesn't have to make any VM calls (unlike System.arraycopy()).

.clone() or Arrays.copyOf()?

Update using jmh

Using jmh, I get similar results, except that clone seems to be marginally better.

Original post

I ran a quick test for performance: clone, System.arrayCopy and Arrays.copyOf have very similar performance (jdk 1.7.06, server vm).

For details (in ms), after JIT:

clone: 68

arrayCopy: 68

Arrays.copyOf: 68

Test code:

public static void main(String[] args) throws InterruptedException,
IOException {
int sum = 0;
int[] warmup = new int[1];
warmup[0] = 1;
for (int i = 0; i < 15000; i++) { // triggers JIT
sum += copyClone(warmup);
sum += copyArrayCopy(warmup);
sum += copyCopyOf(warmup);
}

int count = 10_000_000;
int[] array = new int[count];
for (int i = 0; i < count; i++) {
array[i] = i;
}

// additional warmup for main
for (int i = 0; i < 10; i++) {
sum += copyArrayCopy(array);
}
System.gc();
// copyClone
long start = System.nanoTime();
for (int i = 0; i < 10; i++) {
sum += copyClone(array);
}
long end = System.nanoTime();
System.out.println("clone: " + (end - start) / 1000000);
System.gc();
// copyArrayCopy
start = System.nanoTime();
for (int i = 0; i < 10; i++) {
sum += copyArrayCopy(array);
}
end = System.nanoTime();
System.out.println("arrayCopy: " + (end - start) / 1000000);
System.gc();
// copyCopyOf
start = System.nanoTime();
for (int i = 0; i < 10; i++) {
sum += copyCopyOf(array);
}
end = System.nanoTime();
System.out.println("Arrays.copyOf: " + (end - start) / 1000000);
// sum
System.out.println(sum);
}

private static int copyClone(int[] array) {
int[] copy = array.clone();
return copy[copy.length - 1];
}

private static int copyArrayCopy(int[] array) {
int[] copy = new int[array.length];
System.arraycopy(array, 0, copy, 0, array.length);
return copy[copy.length - 1];
}

private static int copyCopyOf(int[] array) {
int[] copy = Arrays.copyOf(array, array.length);
return copy[copy.length - 1];
}

Java cloning method does not work

The problem is the following:

  • When you clone an array of primitives, the array object and its values are cloned.
  • When you clone an array of objects, the array object is cloned, but the cloned array will contain references to the same objects that were contained in the original array.

Now what does that mean for your case?

  • Your wtour value seems to be an array containing primitive ints, so the first of the two cases above applies. I.e. both the array and it's contents are effectively copied.
  • Your w value however seems to be a multidimensional array of ints. In practice this means that it's actually an array containing array objects, hence the second case applies. Although your top-level array object is copied, the copied array contains references to the same second-level array objects as the original array.

A similar issue and possible solutions are discussed here.

Update

As requested in the comments, a straightforward implementation could look like this. Note that this is completely untested:

public static int[][][] copy(int[][][] source) {
int[][][] target = source.clone();

for (int i = 0; i < source.length; i++) {
target[i] = source[i].clone();

for (int j = 0; j < source[i].length; j++) {
target[i][j] = source[i][j].clone();
}
}

return target;
}

Write a generic method to copy an array

You can use the concept of reflection to write a generic copy method that can determine type at runtime. In a nutshell, reflection is the ability to inspect classes, interfaces, fields and methods at runtime without knowing the names of classes, methods etc at compile time.

java.lang.Reflect together with java.lang.Class comprise the Java Reflection API. This method uses both of those classes and some of their methods to make a generic arrayCopy method that will find out the type for us.

More info: What is reflection and why is it useful?

Syntax that may be unfamiliar

  • Class<?> is using a wildcard operator ? which basically says that we can have a Class object of unknown type - a generic version of class Class.
  • <T> is a generic operator that stands for raw type
  • ArrayThe Array class provides static methods to dynamically create and access Java arrays. i.e. This class contains methods that allow you to set and query the values of array elements, determine the length of the array, and create new instances of arrays. We are going to use Array.newInstance()
  • Methods from reflection API

  • getClass () - returns an array containing Class objects representing all public classes and interfaces that are members of the represented class object.
  • getComponentType() - returns the class representing the component type (what type i.e. int, , etc) of the array.
  • newInstance() - Gets a new instance of an array.
  • private <T> T[] arrayCopy(T[] original) {

    //get the class type of the original array we passed in and determine the type, store in arrayType
    Class<?> arrayType = original.getClass().getComponentType();

    //declare array, cast to (T[]) that was determined using reflection, use java.lang.reflect to create a new instance of an Array(of arrayType variable, and the same length as the original
    T[] copy = (T[])java.lang.reflect.Array.newInstance(arrayType, original.length);

    //Use System and arraycopy to copy the array
    System.arraycopy(original, 0, copy, 0, original.length);
    return copy;
    }


    Related Topics



    Leave a reply



    Submit