Wie klone ich eine generische Liste in Java?

155

Ich habe eine ArrayList<String>, von der ich eine Kopie zurückgeben möchte. ArrayListhat eine Klonmethode mit der folgenden Signatur:

public Object clone()

Wie kann ich das zurückgegebene Objekt nach dem Aufrufen dieser Methode zurücksetzen ArrayList<String>?

Bill die Eidechse
quelle
16
Nein, das ist eine gültige Frage. Java unterstützt keine "echten" Generika mit Laufzeitlöschung und allem, daher können diese Details schwierig sein. Darüber hinaus sind die Cloneable-Schnittstelle und der Methodenmechanismus Object.clone () ähnlich verwirrend.
Outlaw Programmer
OK, ich mache meistens C #, wo das wirklich einfach ist. Bitte lassen Sie mich wissen, wenn ich die Kommentare aus dieser Frage entfernen soll.
Espo
1
Sie können den Kommentar hinterlassen. Ich glaube, meine Änderungen haben erklärt, womit ich Probleme hatte.
Bill the Lizard
2
Ihr Kommentar ist in Ordnung, wenn auch etwas herablassend. Ich stelle mir vor, dass viele Reifen, durch die Java-Entwickler springen müssen, für .NET-Entwickler albern erscheinen.
Outlaw Programmer
1
@Oscar, er möchte klonen und den Klonbefehl nicht aufrufen. Es kann sein, dass es nicht dasselbe ist, wenn der Klon nicht wirklich klont. Ich denke, das ist der Punkt. Dies ist in der Tat eine schwierige Frage.
Rafa

Antworten:

62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();
Vinko Vrsalovic
quelle
43
Dies funktioniert gut für Strings (was in der Frage gestellt wird), aber es ist erwähnenswert, dass ArrayList.clone eine flache Kopie ausführt. Wenn also veränderbare Objekte in der Liste vorhanden sind, werden sie nicht geklont (und geändert) in einer Liste wird die in der anderen Liste auch ändern.
pkaeding
49
Sie sollten die Verwendung von Rohtypen in etwas anderem als Legacy-Code vermeiden. Du bist besser dran ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
cdmckay
20
Es ist schade, dass ArrayList eine # clone-Methode hat, List selbst jedoch nicht. Seufzer.
Rogerdpack
12
Nicht verwandt, aber währenddessen: Verwenden Sie List<String>statt ArrayList<String>auf der linken Seite. So sollten Sammlungen die meiste Zeit verwendet werden.
Christophe Roussy
3
Ich konnte diese Methode nicht verwenden, um eine ArrayList zu duplizieren. Die clone () -Methode wurde nicht erkannt.
Jack
318

Warum sollten Sie klonen wollen? Das Erstellen einer neuen Liste ist normalerweise sinnvoller.

List<String> strs;
...
List<String> newStrs = new ArrayList<>(strs);

Job erledigt.

Tom Hawtin - Tackline
quelle
17
Möglicherweise wissen Sie nicht, um welche Art von Liste es sich handelt. Dies kann eine LinkedList, MyOwnCustomList oder eine Unterklasse von ArrayList sein. In diesem Fall wäre das Neueinstellen einer ArrayList der falsche Typ.
Steve Kuo
51
Würde es mich interessieren, welche Implementierung die ursprüngliche Liste verwendet? Es ist mir wahrscheinlich wichtig, welche Implementierung die neue Liste verwendet.
Tom Hawtin - Tackline
12
@Steve Kuo: Die Signatur ArrayList(Collection<? extends E> c)bedeutet, dass es keine Rolle spielt, welche Art von Liste Sie als Argument verwenden.
cdmckay
3
Ich meine, es ist keine tiefe Kopie, oder?
4
@ YekhezkelYovel Nein, das wäre es nicht.
Tom Hawtin - Tackline
19

Dies ist der Code, den ich dafür verwende:

ArrayList copy = new ArrayList (original.size());
Collections.copy(copy, original);

Hoffnung ist nützlich für Sie

Deutsche
quelle
3
Und vermeiden Sie die Verwendung von ArrayListArrayList<YourObject>
Rohtypen. Also
2
docs.oracle.com/javase/8/docs/api/java/util/… Funktioniert nicht, da die Listengrößen unterschiedlich sind.
MLProgrammer-CiM
Warum würden Sie nicht einfach die Kopierüberladung des ArrayListKonstruktors verwenden?
Tom Hawtin - Tackline
19

Mit Java 8 kann es mit einem Stream geklont werden.

import static java.util.stream.Collectors.toList;

...

List<AnObject> clone = myList.stream().collect(toList());
Simon Jenkins
quelle
Kann wie List <AnObject> ausgeführt werden xy = new ArrayList <> (oldList);
Mirzak
1
Dies ist keine tiefe Kopie, Änderungen an den Elementen einer Liste können in einer anderen gesehen werden
Inchara
2
Wo in der Frage wird angegeben, dass eine tiefe Kopie erforderlich ist? Es wird davon ausgegangen, dass eine andere Sammlung mit denselben Objekten erforderlich ist. Wenn Sie den Weg des tiefen Kopierens beschreiten möchten, öffnen Sie eine ganz neue Dose Würmer.
Simon Jenkins
Nicht wirklich ein Klon, oder? Aus den API-Dokumenten "Es gibt keine Garantien für den Typ, die Veränderlichkeit, die Serialisierbarkeit oder die Thread-Sicherheit der zurückgegebenen Liste". Euw, ich will keinen von denen. toCollectionkann eine bessere Wahl sein.
Tom Hawtin - Tackline
16

Beachten Sie, dass Object.clone () einige Hauptprobleme aufweist und von der Verwendung in den meisten Fällen abgeraten wird. Eine vollständige Antwort finden Sie unter Punkt 11 aus " Effective Java " von Joshua Bloch. Ich glaube, Sie können Object.clone () sicher für primitive Arrays verwenden, aber ansonsten müssen Sie vorsichtig sein, wenn Sie den Klon richtig verwenden und überschreiben. Sie sind wahrscheinlich besser dran, einen Kopierkonstruktor oder eine statische Factory-Methode zu definieren, die das Objekt explizit gemäß Ihrer Semantik klont.

Julien Chastang
quelle
15

Ich denke, dies sollte den Trick mit der Collections-API tun:

Hinweis : Die Kopiermethode wird in linearer Zeit ausgeführt.

//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);
Aaron
quelle
13
Warum nicht einfach die neue ArrayList <String> (oldList) verwenden?
cdmckay
4
Ich glaube, das würde nicht funktionieren, von doc * Die Zielliste muss mindestens so lang sein wie die Quellliste. Wenn es länger ist, bleiben die verbleibenden Elemente in der Zielliste unberührt.
Greg Domjan
@ GregDomjan nicht, dass ich damit einverstanden bin, dass dies der beste Weg ist, aber es ist ein Weg, es zu tun. Um Ihr Problem zu umgehen, ist es so einfach: List <String> newList = new ArrayList <> (oldList.size ());
Nckbrz
1
Ich bin mir nicht sicher, ob es sich um Java 8 handelt, aber selbst bei Angabe der Größe wird immer noch eine Ausnahme angezeigt. IndexOutOfBoundsException: destination.size () <source.size (): 0 <2 on List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); Es scheint, dass die Verwendung copyList.addAll(original);eine gute Alternative ist
Gene Bo,
@GeneBo Die Größe wird nicht angegeben. Es spezifiziert die Kapazität, was eine ganz andere Sache ist. Die Freude an mysteriösen intArgumenten. Ich habe keine Ahnung, warum Sie diese obskure statische Methode anstelle von guten alten verwenden möchten addAll.
Tom Hawtin - Tackline
8

Ich finde, dass die Verwendung von addAll gut funktioniert.

ArrayList<String> copy = new ArrayList<String>();
copy.addAll(original);

Klammern werden anstelle der generischen Syntax verwendet

Allain Lalonde
quelle
5
Das funktioniert für Strings, aber nicht für veränderbare Objekte. Sie werden diese auch klonen wollen.
Jodonnell
Ja, seine Frage ist für Streicher. Und er hat das Problem von Generika, die das Casting in diesem Fall nicht wirklich mögen.
Allain Lalonde
Außerdem führt ArrayList.clone nur einen flachen Klon aus, sodass veränderbare Objekte in der Liste auch mit dieser Methode nicht geklont werden.
pkaeding
Das sollte übrigens ArrayList <String> sein. Außerdem ist es wahrscheinlich besser, die neue ArrayList <String> (Original) zu verwenden, da sie weniger geschrieben und genauso klar ist.
cdmckay
4
Warum nicht einfach benutzen new ArrayList<String>(original)?
user102008
6

Wenn Sie dies möchten, um die Liste in einem Getter zurückgeben zu können, ist es besser, Folgendes zu tun:

ImmutableList.copyOf(list);
Uri Shalit
quelle
4

Um eine generische Schnittstelle wie diese zu klonen, müssen java.util.ListSie sie nur umwandeln. Hier sind Sie ein Beispiel:

List list = new ArrayList();
List list2 = ((List) ( (ArrayList) list).clone());

Es ist ein bisschen knifflig, aber es funktioniert, wenn Sie nur eine ListSchnittstelle zurückgeben können, sodass jeder, nachdem Sie Ihre Liste implementiert haben, wann immer er möchte.

Ich weiß, dass diese Antwort nahe an der endgültigen Antwort liegt, aber meine Antwort beantwortet, wie das alles zu tun ist, während Sie mit dem Listgenerischen Elternteil arbeitenArrayList

Ahmed Hamdy
quelle
2
Sie gehen davon aus, dass es immer eine ArrayList sein wird, die nicht wahr ist
jucardi
@ JuanCarlosDiaz wird, dies ist die Frage gestellt, es ging um ArrayList, also habe ich es für ArrayList beantwortet :)
Ahmed Hamdy
2

Seien Sie beim Klonen von ArrayLists sehr vorsichtig. Das Klonen in Java ist flach. Dies bedeutet, dass nur die Arraylist selbst und nicht ihre Mitglieder geklont werden. Wenn Sie also eine ArrayList X1 haben und diese in X2 klonen, manifestiert sich jede Änderung in X2 auch in X1 und umgekehrt. Wenn Sie klonen, generieren Sie nur eine neue ArrayList mit Zeigern auf dieselben Elemente im Original.

Juan Besa
quelle
2

Dies sollte auch funktionieren:

ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()
pkaeding
quelle
2
List<String> shallowClonedList = new ArrayList<>(listOfStrings);

Denken Sie daran, dass dies nur eine flache, keine tiefe Kopie ist, dh. Sie erhalten eine neue Liste, aber die Einträge sind die gleichen. Dies ist kein Problem für einfache Zeichenfolgen. Es wird schwieriger, wenn die Listeneinträge selbst Objekte sind.

Robert
quelle
1
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();
Jodonnell
quelle
1

Ich bin kein Java-Profi, aber ich habe das gleiche Problem und habe versucht, es mit dieser Methode zu lösen. (Es wird angenommen, dass T einen Kopierkonstruktor hat).

 public static <T extends Object> List<T> clone(List<T> list) {
      try {
           List<T> c = list.getClass().newInstance();
           for(T t: list) {
             T copy = (T) t.getClass().getDeclaredConstructor(t.getclass()).newInstance(t);
             c.add(copy);
           }
           return c;
      } catch(Exception e) {
           throw new RuntimeException("List cloning unsupported",e);
      }
}
Petr
quelle
Es gibt keine Garantie dafür, dass die ListImplementierungsklasse einen öffentlichen Konstruktor ohne Argumente hat. Zum Beispiel können die Lists Renditen Array.asList, List.ofoder List.subListwahrscheinlich nicht.
Tom Hawtin - Tackline