Java ArrayList-Kopie

214

Ich habe eine ArrayList l1Größe 10. Ich ordne l1einen neuen Listenreferenztyp zu l2. Will l1und l2zeige auf dasselbe ArrayListObjekt? Oder ist eine Kopie des ArrayListObjekts zugeordnet l2?

Wenn l2ich bei der Verwendung der Referenz das Listenobjekt aktualisiere, werden auch die Änderungen im l1Referenztyp berücksichtigt.

Beispielsweise:

List<Integer> l1 = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++) {
    l1.add(i);
}

List l2 = l1;
l2.clear();

Gibt es keine andere Möglichkeit, einer neuen Referenzvariablen eine Kopie eines Listenobjekts zuzuweisen, als zwei Listenobjekte zu erstellen und Sammlungen von alt nach neu zu kopieren?

user309281
quelle

Antworten:

459

Ja, die Zuweisung kopiert nur den Wert von l1(der eine Referenz ist) nach l2. Sie beziehen sich beide auf dasselbe Objekt.

Das Erstellen einer flachen Kopie ist jedoch ziemlich einfach:

List<Integer> newList = new ArrayList<>(oldList);

(Nur als ein Beispiel.)

Jon Skeet
quelle
1
Ist es möglich, nur einen Teil einer Arrayliste effizient in eine neue Arrayliste zu kopieren? Beispiel: Kopieren Sie Elemente zwischen Position 5 und 10 von einer Arrayliste in eine andere neue Arrayliste. In meiner Anwendung wäre die Reichweite viel größer.
Ashwin
1
@Ashwin: Nun, es ist eine O (N) -Operation, aber ja ... Sie können verwenden List.subList, um eine "Ansicht" auf einen Abschnitt der ursprünglichen Liste zu erhalten.
Jon Skeet
Was ist, wenn die Array-Listen verschachtelt sind (ArrayList<ArrayList<Object>>)? Würde dies rekursiv Kopien aller untergeordneten ArrayList-Objekte erstellen?
Katze
3
@Cat: Nein ... Dies ist nur eine flache Kopie.
Jon Skeet
1
@ShanikaEdiriweera: Du könntest es fließend mit Streams machen, ja. Der schwierige Teil ist jedoch das Erstellen einer tiefen Kopie, die die meisten Objekte nicht bereitstellen. Wenn Sie einen bestimmten Fall im Auge haben, empfehle ich Ihnen, eine neue Frage mit Details zu stellen.
Jon Skeet
67

Versuchen zu benutzen Collections.copy(destination, source);

Sergii Zagriichuk
quelle
14
Möchten Sie erklären, warum dies vorzuziehen ist new ArrayList<>(source);?
Alex
6
@atc es ist eine weitere Möglichkeit, eine flache Kopie zu erstellen, anstelle der neuen ArrayList () wird ein anderer Algorithmus verwendet und kann für jede List-Implementierung verwendet werden, nicht nur für eine ArrayList, das ist alles :)
Sergii Zagriichuk
14
Diese Methode ist sehr irreführend! Eigentlich ist es Beschreibung. Es heißt: "kopiert Elemente aus einer Quellliste in das Ziel", aber sie werden nicht kopiert! Sie werden referenziert, so dass es nur eine Kopie der Objekte gibt. Wenn sie veränderlich sind, sind Sie in Schwierigkeiten
ACV
5
Nirgendwo in Java-API wird tiefes Klonen von einer Sammlungsklasse durchgeführt
Vikash
Diese Antwort macht nicht viel Sinn. Collections.copyist überhaupt keine Alternative zu new ArrayList<>(source). Tatsächlich Collections.copywird davon ausgegangen, dass der Bereich destination.size()mindestens so groß ist source.size(), und der Bereich wird dann indexweise mit der set(int,E)Methode kopiert . Die Methode fügt dem Ziel keine neuen Elemente hinzu. Lesen Sie den Quellcode, wenn er im Javadoc nicht klar genug ist.
Radiodef
35

Ja l1und l2zeigt auf dieselbe Referenz, dasselbe Objekt.

Wenn Sie eine neue ArrayList basierend auf der anderen ArrayList erstellen möchten, gehen Sie folgendermaßen vor:

List<String> l1 = new ArrayList<String>();
l1.add("Hello");
l1.add("World");
List<String> l2 = new ArrayList<String>(l1); //A new arrayList.
l2.add("Everybody");

Das Ergebnis wird l1weiterhin 2 Elemente und l23 Elemente enthalten.

Alfredo Osorio
quelle
Können Sie bitte den Unterschied zwischen List<String> l2 = new ArrayList<String>(l1)und erklären List<String> l2 = l1?
MortalMan
@MortalMan Der Unterschied besteht darin, dass l2 = new ArrayList <String> (l1) ein völlig neues Objekt ist und das Ändern von l2 l1 nicht beeinflusst, während List <String> l2 = l1 Sie kein neues Objekt erstellen, sondern nur auf dasselbe verweisen Objekt als l1. Wenn Sie also in diesem Fall eine Operation wie l2.add ("Everybody"), l1.size () und l2.size () ausführen, wird 3 zurückgegeben, da beide auf dasselbe Objekt verweisen.
Alfredo Osorio
19

Eine weitere bequeme Möglichkeit, die Werte von src ArrayList nach dest Arraylist zu kopieren, ist folgende:

ArrayList<String> src = new ArrayList<String>();
src.add("test string1");
src.add("test string2");
ArrayList<String> dest= new ArrayList<String>();
dest.addAll(src);

Dies ist das tatsächliche Kopieren von Werten und nicht nur das Kopieren von Referenzen.

Harshal Waghmare
quelle
10
Ich bin mir nicht ganz sicher, ob das richtig ist. Mein Test zeigt das Gegenteil (bezieht sich immer noch auf dasselbe Objekt)
Invertigo
Diese Lösung funktionierte für mich bei der Verwendung von ArrayList mit ArrayAdapter
Albanx
1
Diese Antwort ist falsch. addAll () kopiert nur die Referenzen wie invertigo sagte. Dies ist keine tiefe Kopie.
jk7
Für eine ArrayList <String> ist diese Antwort akzeptabel, da String unveränderlich ist. Versuchen Sie es jedoch mit dem Beispiel des OP, einer ArraList <Integer>, und Sie werden sehen, dass nur Referenzen kopiert werden.
jk7
Nur nicht mein Tag, denke ich. Es stellt sich heraus, dass Klassen wie Integer und Long ebenfalls unveränderlich sind, sodass Harshals Antwort für einfache Fälle wie ArrayList <Integer> und ArrayList <String> funktioniert. Wenn dies fehlschlägt, handelt es sich um komplexe Objekte, die nicht unveränderlich sind.
jk7
8

Es gibt eine Methode addAll (), die zum Kopieren einer ArrayList in eine andere dient.

Sie haben beispielsweise zwei Array-Listen: sourceList und targetList . Verwenden Sie den folgenden Code.

targetList.addAll (sourceList);

Vaibhav Agrawal
quelle
Es werden auch nur Referenzen kopiert.
Vikash
4

Java übergibt keine Objekte, sondern Referenzen (Zeiger) an Objekte. Ja, l2 und l1 sind zwei Zeiger auf dasselbe Objekt.

Sie müssen eine explizite Kopie erstellen, wenn Sie zwei verschiedene Listen mit demselben Inhalt benötigen.

JB Nizet
quelle
3
Wie macht man "eine explizite Kopie"? Ich nehme an, Sie sprechen von einer tiefen Kopie?
Cin316
1

List.copyOf ➙ nicht änderbare Liste

Du hast gefragt:

Gibt es keine andere Möglichkeit, eine Kopie einer Liste zuzuweisen?

Java 9 brachte die List.ofMethoden zur Verwendung von Literalen, um eine nicht modifizierbare Listunbekannte konkrete Klasse zu erstellen .

LocalDate today = LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) ;
List< LocalDate > dates = List.of( 
    today.minusDays( 1 ) ,  // Yesterday
    today ,                 // Today
    today.plusDays( 1 )     // Tomorrow
);

Zusammen mit dem haben wir auch List.copyOf. Auch diese Methode gibt eine nicht modifizierbare Listunbekannte konkrete Klasse zurück.

List< String > colors = new ArrayList<>( 4 ) ;          // Creates a modifiable `List`. 
colors.add ( "AliceBlue" ) ;
colors.add ( "PapayaWhip" ) ;
colors.add ( "Chartreuse" ) ;
colors.add ( "DarkSlateGray" ) ;
List< String > masterColors = List.copyOf( colors ) ;   // Creates an unmodifiable `List`.

Mit "nicht modifizierbar" meinen wir die Anzahl der Elemente in der Liste, und der Objektreferenz, der in jedem Slot als Element gespeichert ist, ist fest. Sie können keine Elemente hinzufügen, löschen oder ersetzen. Der in jedem Element enthaltene Objektbezug kann jedoch veränderlich sein oder nicht .

colors.remove( 2 ) ;          // SUCCEEDS. 
masterColors.remove( 2 ) ;    // FAIL - ERROR.

Sehen Sie diesen Code live auf IdeOne.com .

date.toString (): [2020-02-02, 2020-02-03, 2020-02-04]

colours.toString (): [AliceBlue, PapayaWhip, DarkSlateGray]

masterColors.toString (): [AliceBlue, PapayaWhip, Chartreuse, DarkSlateGray]

Sie haben nach Objektreferenzen gefragt. Wie bereits erwähnt, haben Sie nur eine Liste, wenn Sie eine Liste erstellen und zwei Referenzvariablen (Zeigern) zuweisen. Beide zeigen auf dieselbe Liste. Wenn Sie einen der beiden Zeiger zum Ändern der Liste verwenden, werden die Änderungen später in beiden Zeigern angezeigt, da sich nur eine Liste im Speicher befindet.

Sie müssen also eine Kopie der Liste erstellen. Wenn Sie möchten, dass diese Kopie nicht geändert werden kann, verwenden Sie die List.copyOfin dieser Antwort beschriebene Methode. Bei diesem Ansatz erhalten Sie zwei separate Listen mit Elementen, die einen Verweis auf dieselben Inhaltsobjekte enthalten. In unserem obigen Beispiel, in dem StringObjekte zur Darstellung von Farben verwendet werden, schweben die Farbobjekte beispielsweise irgendwo im Speicher herum. Die beiden Listen enthalten Zeiger auf dieselben Farbobjekte. Hier ist ein Diagramm.

Geben Sie hier die Bildbeschreibung ein

Die erste Liste kann colorsgeändert werden. Dies bedeutet, dass einige Elemente entfernt werden können, wie im obigen Code gezeigt, wo wir das ursprüngliche 3. Element entfernt haben Chartreuse(Index von 2 = Ordnungszahl 3). Und Elemente können hinzugefügt werden. Und die Elemente können geändert werden, um auf andere Stringwie OliveDraboder zu verweisen CornflowerBlue.

Im Gegensatz dazu sind die vier Elemente von masterColorsfestgelegt. Kein Entfernen, kein Hinzufügen und kein Ersetzen einer anderen Farbe. Diese ListImplementierung ist nicht veränderbar.

Basil Bourque
quelle