How to clone a multidimensional array in java?

vancan1ty picture vancan1ty · Feb 2, 2012 · Viewed 29.1k times · Source

Edit 2: Below is a code snippet based on DuffyMo's response that illustrates how to get around the limitations of cloning for multidimensional arrays using System.arraycopy.

import java.util.Arrays;

public class Randar {
public static int[][] arrayMaster = {{6,1}, {10,1}, {1,1}};
private static int[][] arrayChanges = new int[arrayMaster.length][2];

public Randar () {

}
public static void main(String[] args) {
    arrayChanges[0][0] = 0;
    resetArrays(arrayChanges, arrayMaster);
    arrayChanges[0][0] = 0;

    System.out.format("arrayMaster: %s, arrayChanges: %s", Arrays.deepToString(arrayMaster), Arrays.deepToString(arrayChanges));
}


public static void resetArrays(int[][] arrayChanges, int[][] arrayMaster) {
for (int a=0; a< arrayMaster.length; a++) {
System.arraycopy(arrayMaster[a], 0, arrayChanges[a], 0, arrayMaster[a].length);
}
// arrayChanges = arrayMaster.clone(); will NOT work as expected
}
}

[ORIGINAL QUESTION] What's a simple way to (fully) clone a multidimensional array in java? This program illustrates my problem.

import java.util.Arrays;

public class Randar {
public static int[][] arrayMaster = {{6,1}, {10,1}, {1,1}};
static private int[][] arrayChanges = arrayMaster;

public static void main(String[] args) {
    arrayChanges[0][0] = 0;
    resetArrays();

    System.out.format("arrayMaster: %s, arrayChanges: %s",Arrays.deepToString(arrayMaster), Arrays.deepToString(arrayChanges));
}


public static void resetArrays() {
arrayChanges = arrayMaster.clone();
}

}

When the above code is run, arrayMaster changes as well as arrayChanges, contrary to my intentions. Thinking that I could clone each single dimensional array member of arrayMaster, I tried to get around the problem with this:

for (int iter = 0; iter < arrayMaster.length; iter++) {
    arrayChanges[iter] = arrayMaster[iter].clone();
    }

but when I run the code that gives a NullPointerException for some reason. Is writing a method that loops through the individual integer values of the arrays my only option?

Thanks.

EDIT 1: This doesn't fix the problem either.

import java.util.Arrays;

public class Randar {
public int[][] arrayMaster = {{6,1}, {10,1}, {1,1}};
private int[][] arrayChanges = arrayMaster.clone();

public Randar () {

}
public static void main(String[] args) {
    Randar Randar1 = new Randar();
    Randar1.arrayChanges[0][0] = 0;
    resetArrays(Randar1.arrayChanges, Randar1.arrayMaster);
    Randar1.arrayChanges[0][0] = 0;

    System.out.format("arrayMaster: %s, arrayChanges: %s",     Arrays.deepToString(Randar1.arrayMaster), Arrays.deepToString(Randar1.arrayChanges));
}


public static void resetArrays(int[][] arrayChanges, int[][] arrayMaster) {
/*for (int a=0; a< arrayMaster.length; a++) {
System.arraycopy(arrayMaster[a].clone(), 0, arrayChanges[a], 0, arrayMaster[a].length);
} */
arrayChanges = arrayMaster.clone();
}
}

Answer

Adam Mihalcin picture Adam Mihalcin · Feb 2, 2012

When the above code is run, arrayMaster changes as well as arrayChanges, contrary to my intentions.

The line

static private int[][] arrayChanges = arrayMaster;

is the culprit. This line makes arrayChanges and arrayMaster point to the same object, so a change to either one is visible when you access the object from either.

EDIT: What happens whenever you clone one dimension of a multidimensional array

As Eric Lippert explains, an array is conceptually a list of variables. If you just assign another variable to point to the same array a la static private int[][] arrayChanges = arrayMaster;, you haven't changed the set of variables at all. You haven't created any new variables except for arrayChanges, so you haven't gotten more memory from the operating system/JVM, so any change you make to arrayMaster is applied to arrayChanges and vice versa.

Now let's look at a two-dimensional array. In Java, a two-dimensional array is a list of variables that happens to have the property that each one of these variables refers to a one-dimensional array. So, whenever you clone a two-dimensional array, you create a new list of variables, each pointing in the same place that the old variables pointed in. So, you have gained a little in that you can safely write arrayChanges[0] = new int[10] without affecting arrayMaster, but as soon as you start referencing arrayChanges[i][j] you are still referencing the same second-level arrays that arrayMaster references. What you really want in order to deep-copy a two-dimensional array of ints is

public static int[][] deepCopyIntMatrix(int[][] input) {
    if (input == null)
        return null;
    int[][] result = new int[input.length][];
    for (int r = 0; r < input.length; r++) {
        result[r] = input[r].clone();
    }
    return result;
}

To those who may look at this answer in the future: yes, it is better replace int with T here and make the method generic, but for this purpose a more concrete deep copy method is simpler to explain well.