How do I do a deep copy of a 2d array in Java?
Asked Answered
T

7

71

I just got bit by using .clone() on my 2d boolean array, thinking that this was a deep copy.

How can I perform a deep copy of my boolean[][] array?

Should I loop through it and do a series of System.arraycopy's?

Taliped answered 14/10, 2009 at 7:46 Comment(0)
B
72

Yes, you should iterate over 2D boolean array in order to deep copy it. Also look at java.util.Arrays#copyOf methods if you are on Java 6.

I would suggest the next code for Java 6:

public static boolean[][] deepCopy(boolean[][] original) {
    if (original == null) {
        return null;
    }

    final boolean[][] result = new boolean[original.length][];
    for (int i = 0; i < original.length; i++) {
        result[i] = Arrays.copyOf(original[i], original[i].length);
        // For Java versions prior to Java 6 use the next:
        // System.arraycopy(original[i], 0, result[i], 0, original[i].length);
    }
    return result;
}
Backbreaker answered 14/10, 2009 at 7:51 Comment(2)
Note that this does not seem to work for Objects. See #15135604Newsstand
This works because you have 2D array of boolean, a primitive type. But when you have a 2D array of objects, this will not copy or clone the objects. Note that Arrays.copyOf() by itself does a shallow copy.Worthless
E
26

In Java 8 this can be accomplished as a one-liner using lambdas:

<T> T[][] deepCopy(T[][] matrix) {
    return java.util.Arrays.stream(matrix).map(el -> el.clone()).toArray($ -> matrix.clone());
}
Electrophilic answered 26/7, 2015 at 12:49 Comment(4)
Also, consider the code here: https://mcmap.net/q/37099/-java-how-to-make-a-copy-of-an-array-of-objectDetect
I don't believe that code adopts itself easily to 2d arrays but I'd be curious if you managed to do it since I can't get it to work.Elmer
@Elmer What do you mean? The given example deep copies a 2d array.Electrophilic
was referring to JIm G's link. I still don't know how to @JimG Edit: looks like that workedElmer
W
12

I'm a fan of the Arrays utility. It has a copyOf method that will do a deep copy of a 1-D array for you, so you'd want something like this:

//say you have boolean[][] foo;
boolean[][] nv = new boolean[foo.length][foo[0].length];
for (int i = 0; i < nv.length; i++)
     nv[i] = Arrays.copyOf(foo[i], foo[i].length);
Weismannism answered 14/10, 2009 at 7:52 Comment(3)
Note, that this only creates a "deep copy" for primitive types! Arrays.copyOf() itself only creates shallow copies.Schell
Note too that this only works if the columns of all the rows are the same size as the first.Gulf
@NilSuria That is not true. See it working here. Though an argument could be made for the code not working when the original array is empty.Moneychanger
M
9

I've managed to come up with a recursive array deep copy. It seems to work pretty well even for multi dimensional arrays with varying dimension lengths e.g.

private static final int[][][] INT_3D_ARRAY = {
        {
                {1}
        },
        {
                {2, 3},
                {4, 5}
        },
        {
                {6, 7, 8},
                {9, 10, 11},
                {12, 13, 14}
        }
};

Here is the utility method.

@SuppressWarnings("unchecked")
public static <T> T[] deepCopyOf(T[] array) {

    if (0 >= array.length) return array;

    return (T[]) deepCopyOf(
            array, 
            Array.newInstance(array[0].getClass(), array.length), 
            0);
}

private static Object deepCopyOf(Object array, Object copiedArray, int index) {

    if (index >= Array.getLength(array)) return copiedArray;

    Object element = Array.get(array, index);

    if (element.getClass().isArray()) {

        Array.set(copiedArray, index, deepCopyOf(
                element,
                Array.newInstance(
                        element.getClass().getComponentType(),
                        Array.getLength(element)),
                0));

    } else {

        Array.set(copiedArray, index, element);
    }

    return deepCopyOf(array, copiedArray, ++index);
}

EDIT: Updated the code to work with primitive arrays.

Mcghee answered 27/4, 2013 at 19:39 Comment(3)
Edit: Found out myself: import java.lang.reflect.Array;Ursola
This answer has a good idea, but the code unfortunately has some problems that make it not a very good example. (1) array[0].getClass() makes the method unsuitable for arrays of mixed objects such as e.g. new Number[] {1, 2.5}. It should use array.getClass().getComponentType() instead. (2) Using recursion to iterate each array index makes the method unsuitable/unpredictable for large arrays. (3) Uses ++index instead of index+1 for no reason.Bernardo
@Bernardo why does the use of recursion make it unsuitable/unpredictable for large arrays?Sib
R
6

Yes, that's the only way to do it. Neither java.util.Arrays not commons-lang offer deep copy for arrays.

Rosario answered 14/10, 2009 at 7:51 Comment(0)
R
5

You can iterate over this array and perform a series of calls of Arrays.copyOf method:

boolean[][] arr1 = {{true, true}, {false, true}};    // original array
boolean[][] arr2 = Arrays.copyOf(arr1, arr1.length); // shallow copy
boolean[][] arr3 = Arrays.stream(arr1)               // deep copy
        .map(arr -> Arrays.copyOf(arr, arr.length))
        .toArray(boolean[][]::new);

arr1[0][0] = false;

System.out.println(Arrays.deepToString(arr1)); // [[false, true], [false, true]]
System.out.println(Arrays.deepToString(arr2)); // [[false, true], [false, true]]
System.out.println(Arrays.deepToString(arr3)); // [[true, true], [false, true]]

Or you can call Object.clone method:

boolean[][] arr3 = Arrays.stream(arr1)
        .map(boolean[]::clone)
        .toArray(boolean[][]::new);

Or you can create a generic method for this purpose:

static <T> T[][] deepCopy(T[][] matrix) {
    return Arrays.stream(matrix)
            .map(arr -> arr.clone())
            .toArray(s -> matrix.clone());
}

See also: Why does Array.copyOf() mutate the original array in case of 2D Arrays?

Rachaba answered 31/12, 2020 at 21:49 Comment(1)
The object.clone method was key -- someone posted the generic method but it was hard to make a one liner since the variable used needed to be effectively final if you tried to call it outside of a method)Elmer
B
2

Here's a reflective example using java.lang.reflect.Array which is more robust and a bit easier to follow. This method will copy any array, and deeply copies multidimensional arrays.

package mcve.util;

import java.lang.reflect.*;

public final class Tools {
    private Tools() {}
    /**
     * Returns a copy of the specified array object, deeply copying
     * multidimensional arrays. If the specified object is null, the
     * return value is null. Note: if the array object has an element
     * type which is a reference type that is not an array type, the
     * elements themselves are not deep copied. This method only copies
     * array objects.
     *
     * @param  array the array object to deep copy
     * @param  <T>   the type of the array to deep copy
     * @return a copy of the specified array object, deeply copying
     *         multidimensional arrays, or null if the object is null
     * @throws IllegalArgumentException if the specified object is not
     *                                  an array
     */
    public static <T> T deepArrayCopy(T array) {
        if (array == null)
            return null;

        Class<?> arrayType = array.getClass();
        if (!arrayType.isArray())
            throw new IllegalArgumentException(arrayType.toString());

        int length = Array.getLength(array);
        Class<?> componentType = arrayType.getComponentType();

        @SuppressWarnings("unchecked")
        T copy = (T) Array.newInstance(componentType, length);

        if (componentType.isArray()) {
            for (int i = 0; i < length; ++i)
                Array.set(copy, i, deepArrayCopy(Array.get(array, i)));
        } else {
            System.arraycopy(array, 0, copy, 0, length);
        }

        return copy;
    }
}
Bernardo answered 29/7, 2018 at 22:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.