2012-02-02 22 views
10

Edycja 2: Poniżej znajduje się fragment kodu oparty na odpowiedzi DuffyMo, który ilustruje, jak obejść ograniczenia klonowania wielowymiarowych tablic za pomocą System.arraycopy.Jak sklonować tablicę wielowymiarową w języku Java?

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 PYTANIE] Co znajduje się w prosty sposób (w pełni) sklonować wielowymiarowej tablicy w 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(); 
} 

} 

Po uruchomieniu powyższego kodu, arrayMaster zmienia się tak samo jak zmiany tablicy, wbrew moim zamierzeniom. Myśląc, że mogę sklonować każdy pojedynczy element wymiarową tablicę arrayMaster, starałem się obejść problem z tym:

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

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

Dzięki.

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

Odpowiedz

13

Kiedy powyższy kod jest uruchamiany, arrayMaster zmienia jak arrayChanges, w przeciwieństwie do moich intencji.

Linia

static private int[][] arrayChanges = arrayMaster; 

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

EDIT: Co się dzieje, gdy można sklonować jeden wymiar tablicy wielowymiarowej

Jak Eric Lippert explains, tablica jest koncepcyjnie lista zmiennych. Jeśli po prostu przypiszesz inną zmienną, aby wskazała 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 z systemu operacyjnego/JVM, więc każda zmiana wprowadzona w arrayMaster zostanie zastosowana do arrayChanges i na odwrót.

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ę na tym, że możesz bezpiecznie pisać arrayChanges[0] = new int[10] bez wpływu na arrayMaster, ale zaraz po rozpoczęciu odwoływania się do arrayChanges[i][j] nadal odwołujesz się do tych samych tablic drugiego poziomu, które są odniesione do arrayMaster. Co naprawdę chcesz w celu głębokiego kopię dwuwymiarową tablicę int 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; 
} 

Dla tych, którzy mogą spojrzeć na tę odpowiedź w przyszłości: tak, to lepiej wymienić int z T tutaj i sprawiają, że metoda ogólna, ale w tym celu prostsze jest dokładniejsze wyjaśnienie bardziej konkretnej metody głębokiego kopiowania.

+0

Dzięki za 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 [link] (http://www.crazysquirrel.com/computing/java/basics/multidimensional-array-cloning.jspx) – vancan1ty

+0

Problem nie jest statyczny w ogóle. Problem polega na tym, że dwie nazwy "arrayChanges" i "arrayMaster" odnoszą się do tego samego obiektu. Wykonałeś płytką kopię, gdy zadeklarowałeś 'arrayChanges'. Zobacz, na jakim płytkim kopiowaniu jest http://en.wikipedia.org/wiki/Object_copy. –

+0

@ user1184054 Proszę zaksięgować nowo poprawnie napisany kod na pytanie. –

16

clone wykonuje "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 tablicy (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(); 
} 
Powiązane problemy