web-dev-qa-db-ja.com

Javaで多次元配列を複製する方法は?

編集2:以下は、System.arraycopyを使用して多次元配列のクローン作成の制限を回避する方法を示すDuffyMoの応答に基づくコードスニペットです。

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の各1次元配列メンバーのクローンを作成できると考えて、次の問題を回避しようとしました。

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

上記のコードを実行すると、私の意図に反して、arrayMasterがarrayChangesと同様に変更されます。

この線

static private int[][] arrayChanges = arrayMaster;

犯人です。この行により、arrayChangesarrayMasterが同じオブジェクトを指すようになるため、どちらかからオブジェクトにアクセスすると、どちらかへの変更が表示されます。

編集:多次元配列の1つの次元を複製するとどうなりますか

Eric Lippertが説明 のように、配列は概念的には変数のリストです。 static private int[][] arrayChanges = arrayMaster;の同じ配列を指すように別の変数を割り当てるだけの場合は、変数のセットをまったく変更していません。 arrayChanges以外の新しい変数を作成していないため、オペレーティングシステム/ JVMからメモリを取得していないため、arrayMasterに加えた変更はすべてarrayChangesおよびその逆。

次に、2次元配列を見てみましょう。 Javaでは、2次元配列は、これらの変数のそれぞれが1次元配列を参照するというプロパティを持つ変数のリストです。したがって、2次元配列のクローンを作成するたびに、変数の新しいリストを作成します。各リストは、古い変数が指しているのと同じ場所を指します。したがって、arrayChanges[0] = new int[10]を安全に記述できるという点で少し得られています。 arrayMasterに影響を与えることはありませんが、arrayChanges[i][j]の参照を開始するとすぐに、arrayMasterが参照するのと同じ第2レベルの配列を参照します。 intの2次元配列をディープコピーするために本当に必要なのは

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

将来この答えを見るかもしれない人へ:はい、ここでintTに置き換えてメソッドをジェネリックにする方が良いですが、この目的のために、より具体的なディープコピーメソッドはより簡単ですよく説明します。

18
Adam Mihalcin

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