Pytanie Jak sklonować wielowymiarową tablicę w java? [duplikować]


To pytanie już zawiera odpowiedź:

Edytuj 2: Poniżej znajduje się fragment kodu oparty na odpowiedzi DuffyMo, który ilustruje, jak obejść ograniczenia klonowania wielowymiarowych tablic za pomocą 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
}
}

[PYTANIE ORYGINALNE] Jaki jest prosty sposób (w pełni) klonowania wielowymiarowej tablicy w języku Java? Ten program ilustruje mój 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();
}

}

Kiedy powyższy kod jest uruchamiany, arrayMaster zmienia się tak samo jak zmiany tablicy, wbrew moim intencjom. Myśląc, że mogę sklonować każdego elementu tablicy jednowymiarowych arrayMaster, starałem się obejść problem z tym:

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

ale gdy uruchamiam kod, który z jakiegoś powodu daje wyjątek NullPointerException. Czy napisanie metody, która pętli przez poszczególne wartości całkowite tablic mojej jedynej opcji?

Dzięki.

EDIT 1: To również nie rozwiązuje problemu.

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

11
2018-02-02 02:01


pochodzenie




Odpowiedzi:


Kiedy powyższy kod jest uruchamiany, arrayMaster zmienia się tak samo jak zmiany tablicy, wbrew moim intencjom.

Linia

static private int[][] arrayChanges = arrayMaster;

jest winowajcą. Ta linia sprawia arrayChanges i arrayMaster wskaż ten sam obiekt, więc zmiana jednego z nich jest widoczna po uzyskaniu dostępu do obiektu.

EDYCJA: Co dzieje się, gdy klonujesz jeden wymiar wielowymiarowej tablicy

Tak jak Eric Lippert wyjaśnia, tablica jest konceptualnie listą zmiennych. Jeśli po prostu przypiszesz inną zmienną, wskaż tę samą tablicę a la static private int[][] arrayChanges = arrayMaster;, w ogóle nie zmieniłeś zestawu zmiennych. Nie utworzyłeś żadnych nowych zmiennych z wyjątkiem arrayChanges, więc nie otrzymałeś więcej pamięci od systemu operacyjnego / maszyny JVM, więc każdą zmianę wprowadzisz arrayMaster jest stosowane do arrayChanges i wzajemnie.

Teraz spójrzmy na dwuwymiarową tablicę. W Javie dwuwymiarowa tablica jest listą zmiennych, które mają właściwość, że każda z tych zmiennych odnosi się do jednowymiarowej tablicy. Tak więc, gdy klonujesz dwuwymiarową tablicę, tworzysz nową listę zmiennych, z których każda wskazuje to samo miejsce, na które wskazywały stare zmienne. Tak więc zyskałeś trochę w tym, że możesz bezpiecznie pisać arrayChanges[0] = new int[10] bez wpływu arrayMaster, ale zaraz po rozpoczęciu tworzenia odnośników arrayChanges[i][j] nadal odwołujesz się do tych samych tablic drugiego poziomu arrayMaster referencje. To, czego naprawdę chcesz, aby głęboko skopiować dwuwymiarową tablicę intów jest

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

Tym, którzy mogą spojrzeć na tę odpowiedź w przyszłości: tak, lepiej jest zastąpić int z T tutaj i uczynić tę metodę ogólną, ale w tym celu prostsza do wyjaśnienia jest bardziej konkretna metoda głębokiego kopiowania.


14
2018-02-02 02:06



Dzięki za twoją odpowiedź. Przepisałem to, aby powiązać wszystkie wartości w jedną klasę i nie chciałem zadeklarować instancji obiektu, więc tak, to był problem. Jednak problem utrzymuje się nawet przy poprawnie napisanym kodzie, jak opisano tutaj połączyć - vancan1ty
Problem w ogóle nie jest statyczny. Problem polega na tym, że te dwie nazwy arrayChanges i arrayMaster odnoszą się do tego samego obiektu. Zrobiłeś płytką kopię, gdy zadeklarowałeś arrayChanges. Widzieć en.wikipedia.org/wiki/Object_copy na jakie płytkie kopiowanie. - Adam Mihalcin
@ user1184054 Proszę wstaw swój nowo poprawnie napisany kod do pytania. - Adam Mihalcin
Wysłałem kod, który ustawia arrayChanges = arrayMaster.clone (). To też nie działa ... Myślę, że problem polega na tym, że kiedy klonujesz tablicę wielowymiarową, jeden poziom tablicy jest przekazywany przez wartość, a drugi przez odniesienie. Jestem początkującym, oczywiście ... - vancan1ty
To jest błąd w odpowiedzi. Pętla for: for (int r = 0; r <input.length; i ++) {powinna być zapisana jako: for (int r = 0; r <input.length; r ++) { - David


clone robi "płytką" kopię. Oznacza to, że najbardziej zewnętrzna tablica jest powielana, ale zapisane w niej wartości pozostają niezmienione. Więc jeśli masz A1 = {B1, B2, B3} i klonujesz to do A2, początkowa zawartość A2 będzie {B1, B2, B3}. Jeśli zmienisz A1 na {C1, B2, B3}, A2 pozostanie niezmienione, ale jeśli zmienisz zawartość B1 (bez jej zastępowania), A2 "zobaczy" tę zmianę.

Aby osiągnąć to, co chcesz, musisz przejść przez zewnętrzną tablicę i clone elementy tej zewnętrznej macierzy (które są wewnętrznymi tablicami).

Pidgin Java:

int[][] A2 = A1.clone();
for (int i = 0; i < A2.length; i++) {
    A2[i] = A2[i].clone();
}

16
2018-02-02 03:08