2013-08-21 37 views
15

Wydaje się, że istnieje wiele nieporozumień i różnych opinii na ten temat ([1] i inne źródła) na temat tego, czy Arrays.copyOf wytworzy głęboką lub płytką kopię.Czy Arrays.copyOf tworzy płytką lub głęboką kopię?

Badanie to sugeruje, że kopia jest głęboka

String[] sourceArray = new String[] { "Foo" }; 
String[] targetArray = java.util.Arrays.copyOf(sourceArray, 1); 

sourceArray[0] = "Bar"; 

assertThat(targetArray[0]).isEqualTo("Foo"); // passes 

Badanie to sugeruje, że kopia jest płytkie:

String[][] sourceArray = new String[][] { new String[] { "Foo" } }; 
String[][] targetArray = java.util.Arrays.copyOf(sourceArray, 1); 

sourceArray[0][0] = "Bar"; 

assertThat(targetArray[0][0]).isEqualTo("Foo"); // fails 

Czy rozwiązanie po prostu, że głęboka kopia wymiar najwyższego poziomu jest wykonany, ale inne wymiary to płytka kopia? Jaka jest prawda?

[1] How do I do a deep copy of a 2d array in Java?

+0

Prawdopodobnie kolejne wystąpienie interlinii z ciągami – Matthias

+0

@ Matthias: Nie sądzę. Ponieważ "Foo" jest dosłowne, zostanie internowane; testy zakładają, że. Jeśli to założenie jest poprawne, wówczas testy sprawdzają, czy element docelowy został zmieniony przez '=" Pasek "' odpowiedniego elementu źródłowego. – ToolmakerSteve

+0

Nie widzę, gdzie testy mają jakiekolwiek założenie, czy łańcuchy są internalizowane.Nie widzę żadnych testów tożsamości, widzę tylko testy równości. Wyniki będą identyczne dla płytkich i głębokich kopii, ponieważ testy równości nie mogą się różnić między płytkimi i głębokimi kopiami. Trzeba mieć testy tożsamości, aby odróżnić płytkie i głębokie kopie. –

Odpowiedz

22

Wytwarza płytki kopiowania, to znaczy nowego tablicy, która zawiera „stare” (odniesienie do tych samych obiektów, to nie są kopiowane).

W szczególności, jeśli macie zagnieżdżone tablice, nie zostaną one skopiowane. Otrzymasz nową tablicę, której "najwyższy poziom" wskazuje na te same tablice "drugiego poziomu" co oryginał. Wszelkie zmiany w tych zagnieżdżonych tablicach będą odzwierciedlane zarówno w kopii, jak iw oryginale.

Badanie to sugeruje, że kopia jest głęboka

Nie, tak nie jest. Przypisanie nowego obiektu do "oryginalnej" tablicy nie ma wpływu na kopię. W końcu jest to kopia.

Jest to taka sama sytuacja jak:

String x = "foo"; 
String y = x; 
x = "bar"; 

assertEquals(y, "foo"); 

Nie "głęboko kopiowania" tutaj.

4

Formularz Java Doc

.... dwie tablice zawierają identyczne wartości.

Tak więc w przypadku tablicy zawierającej odniesienie kopiowane jest tylko odniesienie, a nie rzeczywisty obiekt. Co oznacza płytką kopię.

-4

To jest głęboka kopia. Wydaje się płytkie w przypadku Ciągów, ponieważ pod osłonami, Ciągi są Singletonami. JVM ma pulę pamięci dla Strings i tworzy tylko jedną kopię każdego unikalnego ciągu. Dlatego zawsze otrzymujesz kopię odwołania do tego ciągu. Poniższy przykład pokazuje, że została wykonana głęboka kopia dla obiektu klasy. Gdy oryginalna tablica zostanie zmieniona, kopia się nie zmieni.

public class ArrayTest {

public static void main(String [] args) { 
    Object [] objs = new Object[1]; 
    objs[0] = new Object(); 
    System.out.println("Original: " + objs[0].toString()); 

    Object [] copy = java.util.Arrays.copyOf(objs, 1); 
    objs[0] = new Object(); 
    System.out.println("copy, after original reassigned: " + 
    copy[0].toString()); 
    System.out.println("Original, after reassigned: " + 
    objs[0].toString()); 
} 

}

+1

To nie jest głęboka kopia, a głęboka kopia wymagałaby robienia kopii wszystkich obiektów przechowywanych w samej tablicy. Arrays.copyOf tego nie robi; po prostu tworzy kopie referencji - płytkiej kopii –

+0

Ponadto argument, dlaczego tablica łańcuchów zachowałaby się inaczej niż inne obiekty, jest po prostu błędna. Tak, literały łańcuchowe są internowane. Nie, to nie powoduje, że łańcuchy zachowują się inaczej niż jakikolwiek inny obiekt (odniesienie). Prawdopodobnie ten odbierający został wprowadzony w błąd przez powoływanie się na kod zapytania na ciąg znaków literałów, który był niezbędny do przejścia pierwszego testu. Pytanie byłoby bardziej przejrzyste, gdyby wstawiono słowo "Foo" do zmiennej, a następnie używano tej zmiennej wszędzie. – ToolmakerSteve

+0

Przykro mi to mówić, twoja odpowiedź jest błędna na więcej niż jeden sposób. Kopia jest płytką kopią. Ciągi nie są zinternalizowane zawsze, tylko w szczególnych przypadkach. Na przykład w 'String foo =" Foo "; String foo2 = new String (foo); ',' foo2' odnosi się do nowej kopii. I nie można wywnioskować z przesłoniętej metody 'Object.equals()', czy kopia jest głęboka, czy płytka, ponieważ jest to coś, co 'Object.equals()' ukrywa z definicji. –

1

'Shallow' lub 'głębokie' - i to jest kwestia, że ​​widzę nikt definiowania precyzyjnie - metoda Arrays.copyOf(..) robi w praktyce stworzyć kopię tablicy źródłowej, która pozostaje niezmieniona przez zmiany w tablicy źródłowej.

Weźmy następujący prosty przykład z int tablic:

import java.util.Arrays; 

public class DeepCopyTest 
{ 

    public static void main(String[] args) 
    { 
     int[] source = { 1, 2, 3, 4, 5, 6}; 
     int[] target = new int[source.length]; 
     // Copy target from source via Arrays.copyOf(..) method : 
     target = Arrays.copyOf(source, 6); 
     // Check for equality : 
     System.out.println("Source1 : " + Arrays.toString(source)); 
     System.out.println("Target1 : " + Arrays.toString(target)); 
     // Increment all entries in source array : 
     for(int i = 0; i < source.length; i++) 
     { 
      source[i] = source[i] +1; 
     } 
     // See if target is affected : 
     System.out.println("Source2 : " + Arrays.toString(source)); 
     System.out.println("Target2 : " + Arrays.toString(target)); 

    } 

} 

// OUTPUT 
// ------ 
Source1 : [1, 2, 3, 4, 5, 6] 
Target1 : [1, 2, 3, 4, 5, 6] 
Source2 : [2, 3, 4, 5, 6, 7] 
Target2 : [1, 2, 3, 4, 5, 6] 

W praktyce, gdy ludzie szukają „głębokie” kopię tablicy, oni po prostu chcą coś, co jest niezależne od zmian w oryginale.

Ta metoda Arrays.copyOf (..) "daje im to.

również pierwotne typu macierze, łańcuch tablice obiektów zachowywać również jak w powyższym przykładzie, dając wyjściowy jak:

Source1 : [a, b, c, d, e, f] 
Target1 : [a, b, c, d, e, f] 
Source2 : [a1, b1, c1, d1, e1, f1] 
Target2 : [a, b, c, d, e, f] 

gdy początkowe wpisów tablicy źródłem są łączone przez „1”.

Działa również dla tablic obiektów w tym sensie, że cel nie jest już powiązany ze źródłem, gdy ten drugi jest ponownie przypisywany. ale patrząc na wyjściu do pierwszego elementu obu tablicach po skopiowaniu, a następnie po zmianie źródła [0] odsłania pełną prawdę:

Source1 : [email protected] 
Target1 : [email protected] 
Source2 : [email protected] 
Target2 : [email protected] 

Po oryginalna tablica źródło jest kopiowany, elementy docelowe po prostu zostały wskazane do dowolnych wartości, które są obecnie przechowywane w ich źródłowych odpowiednikach. Dla celu [0] jest to zawartość adresu pamięci 1db9742 - który jest również tym samym źródłem podtrzymania adresu pamięci [0]. . . .

A powód otrzymujemy oddzielania między źródłem a celem po źródła [0] zmiany przypisania Wynika to z faktu, że instrukcja przypisania

source[0] = new Object(); 

prostu powoduje odniesienie pamięci przechowywanych w źródle [0], aby zostać zmienionym na nową lokalizację, gdy wskazywany jest nowy Obiekt. Nie jest to przecież prawdziwa głęboka kopia w czystym tego słowa znaczeniu, chociaż w wielu przypadkach daje ona koderowi te same korzyści, co głęboka kopia.

W przypadku tablic danych pierwotnych metoda Arrays.copyOf (..) nie może kopiować odniesień, ponieważ nie są one używane dla elementów pierwotnych. Po prostu kopiuje wartości elementu źródłowego do elementów docelowych. Znowu mamy ten sam efekt, co głęboka kopia kosztem operacji wymagającej znacznie mniej kodu niż w przypadku kopii głębokiej.

Array.copyOf (..) to "tania" głęboka kopia zarówno dla macierzy obiektów pierwotnych, jak i jednodomenowych. Ale jakakolwiek tablica danych jest bardziej złożona i została znaleziona.

Może powinien być nazywany pół-głęboką kopią.

0

Tworzy to płytką kopię, ponieważ ponieważ java używa parametru według wartości, kopie wszystkich zmiennych są dostępne w sklonowanym obiekcie, jednak dla typu odniesienia tworzona jest zmienna kopia adresu i wskazuje ten sam obiekt, do którego odwołuje się oryginalna tablica, więc gdy skopiowany obiekt jest modyfikowany oryginalny obiekt w tablicy również jest aktualizowany. patrz kod poniżej.

import java.util.*; 
import java.lang.*; 
import java.io.*; 

/* Name of the class has to be "Main" only if the class is public. */ 
class ArraysCopyOfDemo 
{ 
    public static void main (String[] args) throws java.lang.Exception 
    { 
     Object[] originalArray= new Object[1]; 
     Employee e1= new Employee("Salman","Khan"); 
     originalArray[0]=e1; 
     System.out.println("Original Array content printed "); 
     printArray(originalArray); 

     Object[] copiedArray=originalArray.clone(); 
     System.out.println("Copied Array content printed "); 
     printArray(copiedArray); 
     System.out.println("Copied Array content modified "); 
     Employee CopiedEmp1= (Employee)copiedArray[0]; 
     CopiedEmp1.setFirstname("Amir"); 
     System.out.println("Copied Array content printed "); 
     printArray(copiedArray); 
     System.out.println("Original Array content printed to verify shallow copy or deep copy"); 
     printArray(originalArray); 
    } 
    private static void printArray(Object[] arrays){ 
     for(Object emp:arrays){ 
      System.out.print(((Employee)emp).getFirstname() + " "); 
      System.out.print(((Employee)emp).getLastname()); 
      System.out.println(); 
     } 
    } 
} 
class Employee implements Cloneable{ 
    private String firstname; 
    private String lastname; 
    public Employee(String firstname,String lastname){ 
     this.firstname=firstname; 
     this.lastname=lastname; 
    } 
    public String getFirstname(){ 
     return firstname; 
    } 
    public String getLastname(){ 
     return lastname; 
    } 
    public void setFirstname(String firstname){ 
     this.firstname=firstname; 
    } 
    public void setLirstname(String lastname){ 
     this.lastname=lastname; 
    } 

} 

O/p 
Original Array content printed 
Salman Khan 
Copied Array content printed 
Salman Khan 
Copied Array content modified 
Copied Array content printed 
Amir Khan 
Original Array content printed to verify shallow copy or deep copy 
Amir Khan 
Powiązane problemy