2013-08-21 13 views
15

Es scheint eine Menge Verwirrung und unterschiedliche Meinungen darüber draußen ([1] und andere Quellen) darüber zu geben, ob Arrays.copyOf eine tiefe oder flache Kopie produzieren wird.Führt Arrays.copyOf eine seichte oder eine tiefe Kopie?

Dieser Test zeigt, dass die Kopie tief ist:

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

sourceArray[0] = "Bar"; 

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

Dieser Test zeigt, dass die Kopie flach ist:

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 

die Lösung ist einfach, dass eine tiefe Kopie der Top-Level-Dimension ist gemacht, aber andere Dimensionen sind eine flache Kopie? Was ist die Wahrheit?

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

+0

Wahrscheinlich nur ein weiteres Auftreten von Internierung von Strings – Matthias

+0

@Matthias: Ich kann nicht so denken. Da "Foo" ein Literal ist, wird es interniert; Die Tests gehen davon aus. Wenn diese Annahme richtig ist, dann untersuchen die Tests, ob das Zielelement durch '=" Bar "' des entsprechenden Quellelements geändert wurde. – ToolmakerSteve

+0

Ich sehe nicht, wo die Tests eine Annahme darüber machen, ob Strings internalisiert werden oder nicht.Ich sehe keine Identitätstests, ich sehe nur Gleichheitstests. Die Ergebnisse wären für flache und tiefe Kopien identisch, da Gleichheitstests nicht zwischen flachen und tiefen Kopien unterscheiden können. Man benötigt Identitätsprüfungen, um zwischen flachen und tiefen Kopien zu unterscheiden. –

Antwort

22

Es erzeugt eine flache Kopie, das heißt einen neuen Array, das „alten“ Verweis enthält (auf dieselben Objekte, die nicht kopiert werden).

Insbesondere wenn Sie verschachtelte Arrays haben, werden diese nicht kopiert. Sie werden nur ein neues Array bekommen, dessen "Top Level" auf die gleichen "Second Level" Arrays wie das Original zeigt. Alle Änderungen in diesen verschachtelten Arrays werden sowohl in Kopie als auch in Original wiedergegeben.

Dieser Test zeigt, dass die Kopie tief ist:

Nein, tut es nicht. Wenn Sie dem Array "original" ein neues Objekt zuweisen, wirkt sich dies nicht auf die Kopie aus. Es ist schließlich eine Kopie.

Dies ist die gleiche Situation wie:

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

assertEquals(y, "foo"); 

Nein "tiefe Kopie" hier.

4

Formular Java Doc

.... Die beiden Arrays werden identische Werte enthalten.

Also im Falle von Array mit Referenz wird nur die Referenz kopiert und nicht das eigentliche Objekt. Was eine flache Kopie bedeutet.

-4

Es ist eine tiefe Kopie. Im Falle von Strings scheint es oberflächlich zu sein, weil unter den Deckeln Strings Singletons sind. Die JVM verfügt über einen Speicherpool für Strings und erstellt nur eine Kopie jeder eindeutigen Zeichenfolge. Sie erhalten also immer eine Kopie der Referenz auf diese Zeichenfolge. Das folgende Beispiel zeigt, dass eine tiefe Kopie für das Klassenobjekt erstellt wurde. Wenn das ursprüngliche Array geändert wird, ändert sich die Kopie nicht.

public class Klasse Array {

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

Es handelt sich nicht um eine tiefe Kopie, sondern um eine tiefe Kopie, bei der Kopien aller im Array gespeicherten Objekte erstellt werden. Arrays.copyOf macht das nicht; es macht einfach Kopien der Referenzen - d. h. eine flache Kopie –

+0

Zusätzlich ist das Argument, warum sich ein Array von Strings anders als andere Objekte verhält, einfach falsch. Ja, Zeichenfolgenliterale sind interniert. Nein, das führt nicht dazu, dass sich Strings anders verhalten als jedes andere (Referenz-) Objekt. Möglicherweise wurde dieser Beantworter durch die Abhängigkeit des Fragecodes vom String-Interning von Literalen, der für den ersten Test notwendig war, in die Irre geführt. Die Frage wäre klarer gewesen, wenn man "Foo" in eine Variable gesetzt und diese Variable dann überall benutzt hätte. – ToolmakerSteve

+0

Entschuldigen Sie bitte, dass Ihre Antwort in mehr als einer Hinsicht falsch ist. Die Kopie ist eine flache Kopie. Strings werden nicht immer, nur in besonderen Fällen, verinnerlicht. Zum Beispiel in 'String foo =" Foo "; String foo2 = new String (foo); ',' foo2' verweist auf eine neue Kopie. Und es ist nicht möglich, aus einer überschriebenen 'Object.equals()' -Methode zu schließen, ob eine Kopie tief oder flach ist, weil sich das 'Object.equals()' per Definition verbirgt. –

1

'Shallow' oder 'tief' - und das ist eine Sache, die ich niemand sehen, genau zu definieren - die Methode Arrays.copyOf(..) in der Praxis produziert eine Kopie des Quell-Arrays, das von Änderungen am Quell-Array nicht betroffen ist.

Nehmen Sie das folgende einfache Beispiel mit int-Arrays:

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] 

In der Praxis, wenn die Menschen eine „tiefe Kopie“ eines Arrays suchen, sie nur wollen etwas, das durch Änderungen an der ursprünglichen nicht beeinträchtigt wird.

Und diese Arrays.copyOf (..) `Methode gibt ihnen dies.

Neben Urtyp Arrays Objekt String-Arrays auch als das obige Beispiel verhalten, wie die Aufgabe Ausgang:

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] 

wenn die anfänglichen Quellenarray Einträge von „1“ verkettet sind.

Es funktioniert auch für Objekt-Arrays in dem Sinne, dass das Ziel nicht mehr an die Quelle gebunden ist, wenn letztere neu zugewiesen wird. ABER am Ausgang für das erste Element der beiden Arrays nach dem Kopieren sucht und dann nach Quelle zu verändern [0] zeigt die volle Wahrheit:

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

Nachdem der ursprüngliche Quelle Array kopiert wird, einfach die Zielelemente hingewiesen worden zu welchen Werten auch immer in ihren Quellgegenstücken gehalten werden. Für Ziel [0] ist es der Inhalt der Speicheradresse 1db9742 - dies ist auch die gleiche Speicheradresse, die die Quelle [0] enthält. . . .

Und der Grund, warum wir eine Entbindungs ​​zwischen Quelle und Ziel nach Quelle erhalten [0] zugewiesen wird, ist aufgrund der Tatsache, dass die Zuweisungsanweisung

source[0] = new Object(); 

einfach bewirkt, dass die Speicherreferenz in Quelle gehalten [0] bis an einem neuen Ort geändert werden, während auf ein neues Objekt gezeigt wird. Es handelt sich also nicht um eine echte tiefe Kopie im reinen Sinne, obwohl es in vielen Fällen dem Programmierer die gleichen Vorteile wie eine tiefe Kopie gibt.

Bei Arrays primitiver Daten kann die Methode "Arrays.copyOf (..)" keine Referenzen kopieren, da diese nicht für Grundelemente verwendet werden. Es kopiert nur die Quellelementwerte in die Zielelemente. Wiederum haben wir dieselbe Wirkung wie eine tiefe Kopie auf Kosten einer Operation, die viel weniger Code benötigt als für eine tiefe Kopie.

Also Arrays.copyOf (..) ist eine 'billige' tiefe Kopie für primitive und 1-D Object-Arrays. Aber jedes Datenfeld ist komplexer und es wird herausgefunden.

Vielleicht sollte es eine halbtiefe Kopie genannt werden.

0

Es erstellt Shallow Copy, aber da Java Parameter nach Wert verwendet, sind die Kopien aller Variablen in geklonten Objekten verfügbar, für Referenztypen wird jedoch eine variable Kopie der Adresse erstellt und verweist auf dasselbe Objekt, auf das vom ursprünglichen Array verwiesen wird kopiertes Objekt wird modifiziert Originalobjekt im Array wird ebenfalls aktualisiert. siehe Code unten.

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 
Verwandte Themen