如何在Java中克隆多维数组?

16

编辑2:以下是一个基于DuffyMo的回答的代码片段,演示了如何使用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
}
}

[翻译内容] 有没有一种简单的方式在java中克隆(完整地)多维数组? 这个程序说明了我的问题。

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();
}

}
当运行上述代码时,arrayMaster和arrayChanges都会改变,这与我的意图相反。我尝试通过克隆arrayMaster的每一个单维数组成员来解决这个问题。
for (int iter = 0; iter < arrayMaster.length; iter++) {
    arrayChanges[iter] = arrayMaster[iter].clone();
    }

但是当我运行这段代码时,由于某种原因它会导致NullPointerException异常。是否编写一个循环遍历数组中的每个整数值是唯一的选择?

谢谢。

编辑1:这也无法解决问题。

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();
}
}
2个回答

21
当运行上述代码时,arrayMaster和arrayChanges都会发生变化,这与我的意愿相反。
这一行
static private int[][] arrayChanges = arrayMaster;

罪魁祸首是这行代码。这一行使arrayChangesarrayMaster指向同一个对象,因此对任意一个对象的更改都可以从另一个对象中访问到。

编辑:当你克隆多维数组的一维时会发生什么

正如Eric Lippert所解释的那样,数组在概念上是变量列表。如果你只是像这样将另一个变量分配给同一数组:static private int[][] arrayChanges = arrayMaster;,那么你并没有改变变量集合。你没有创建除arrayChanges之外的任何新变量,因此你没有从操作系统/JVM获得更多内存,因此你对arrayMaster所做的任何更改都会应用于arrayChanges,反之亦然。

现在让我们来看看二维数组。在Java中,二维数组是一个变量列表,每个变量恰好指向一个一维数组。因此,当你克隆一个二维数组时,你创建了一个新的变量列表,每个变量都指向旧变量指向的同一个位置。因此,你可以安全地写arrayChanges[0] = new int[10]而不影响arrayMaster,但是一旦你开始引用arrayChanges[i][j],你仍然在引用arrayMaster引用的同一二级数组。如果你想要深度复制一个int类型的二维数组,你真正需要的是:

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;
}

对于未来可能查看此答案的人:是的,在这里用T代替int并使方法通用更好,但为了这个目的,一个更具体的深拷贝方法更容易解释清楚。


感谢您的回复。我将所有值重写为一个类,并且不想声明对象实例,所以这确实是个问题。然而,即使代码正确编写,问题仍然存在,如此处所述[链接](http://www.crazysquirrel.com/computing/java/basics/multidimensional-array-cloning.jspx)。 - vancan1ty
问题并不在于静态。问题在于两个名称 arrayChangesarrayMaster 引用了同一个对象。当您声明 arrayChanges 时,您进行了浅拷贝。请参阅 http://en.wikipedia.org/wiki/Object_copy 了解浅拷贝的含义。 - Adam Mihalcin
@user1184054请将您新正确编写的代码发布到问题中。 - Adam Mihalcin
1
答案中有一个错误。for循环:for(int r = 0; r < input.length; i++){应该写成: for(int r = 0; r < input.length; r++){ - David
@David 错别字已经修正。谢谢! - Adam Mihalcin
显示剩余2条评论

21
clone方法进行的是“浅拷贝”,即外部数组会被复制,但其中存储的值不会改变。因此,如果你有A1 = { B1,B2,B3 },并将其克隆到A2,那么A2的初始内容将为{ B1,B2,B3 }。如果你将A1更改为{ C1,B2,B3 },那么A2将保持不变,但如果你更改B1的内容(而不是替换它),那么A2将“看到”这个更改。
要实现你想要的效果,必须循环访问外部数组,并对该外部数组中的元素(即内部数组)进行clone操作。
简化Java:
int[][] A2 = A1.clone();
for (int i = 0; i < A2.length; i++) {
    A2[i] = A2[i].clone();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接