wake-up-neo.net

Wie kann ich ArrayList und auch dessen Inhalt klonen?

Wie kann ich eine ArrayList und auch deren Elemente in Java klonen?

Zum Beispiel habe ich:

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....

Und ich würde erwarten, dass Objekte in clonedList nicht mit denen in der Hundeliste übereinstimmen.

239
palig

Sie müssen die Elemente iterieren und nacheinander klonen, wobei die Klone im Ergebnis-Array abgelegt werden.

public static List<Dog> cloneList(List<Dog> list) {
    List<Dog> clone = new ArrayList<Dog>(list.size());
    for (Dog item : list) clone.add(item.clone());
    return clone;
}

Damit dies funktioniert, müssen Sie natürlich Ihre Dog-Klasse installieren, um die Cloneable-Schnittstelle zu implementieren und die clone()-Methode zu überschreiben.

181
Varkhan

Ich persönlich würde Dog einen Konstruktor hinzufügen:

class Dog
{
    public Dog()
    { ... } // Regular constructor

    public Dog(Dog dog) {
        // Copy all the fields of Dog.
    }
}

Dann wiederholen Sie einfach (wie in Varkhans Antwort gezeigt):

public static List<Dog> cloneList(List<Dog> dogList) {
    List<Dog> clonedList = new ArrayList<Dog>(dogList.size());
    for (Dog dog : dogList) {
        clonedList.add(new Dog(dog));
    }
    return clonedList;
}

Ich habe den Vorteil, dass Sie nicht mit dem kaputten klonbaren Zeug in Java herumspielen müssen. Es entspricht auch der Art und Weise, wie Sie Java-Sammlungen kopieren.

Eine andere Option könnte sein, eine eigene ICloneable-Schnittstelle zu schreiben und diese zu verwenden. Auf diese Weise könnten Sie eine generische Methode zum Klonen schreiben.

180
cdmckay

Alle Standardsammlungen verfügen über Kopierkonstruktoren. Benutze sie.

List<Double> original = // some list
List<Double> copy = new ArrayList<Double>(original); //This does a shallow copy

clone() wurde mit mehreren Fehlern entwickelt (siehe diese Frage ). Daher sollten Sie diese vermeiden.

Ab Effective Java 2nd Edition , Element 11: Überschreiben des Klons mit Bedacht

Angesichts all der Probleme, die mit Cloneable verbunden sind, kann man mit Sicherheit sagen dass andere Schnittstellen es nicht erweitern sollten, und dass die Klassen entworfen für Vererbung (Punkt 17) sollte es nicht implementieren. Durch Einige erfahrene Programmierer entscheiden sich einfach dafür, niemals Überschreiben Sie die Klonmethode und rufen Sie sie niemals auf, außer vielleicht Arrays kopieren. Wenn Sie eine Klasse für die Vererbung entwerfen, beachten Sie, dass if Sie entscheiden sich dafür, keine gut verhaltene geschützte Klonmethode bereitzustellen, es Es ist für Unterklassen unmöglich, Cloneable zu implementieren.

Dieses Buch beschreibt auch die vielen Vorteile, die Copy-Konstruktoren gegenüber Cloneable/Clone haben.

  • Sie verlassen sich nicht auf einen risikoreichen extralinguistischen Objekterstellungsmechanismus .__
  • Sie verlangen nicht die nicht durchsetzbare Einhaltung dünn dokumentierter Konventionen
  • Sie stehen nicht in Konflikt mit der korrekten Verwendung der endgültigen Felder
  • Sie werfen keine unnötigen geprüften Ausnahmen
  • Sie benötigen keine Abgüsse.

Berücksichtigen Sie einen weiteren Vorteil der Verwendung von Kopierkonstruktoren: Angenommen, Sie haben einen HashSet s und möchten diesen als TreeSet kopieren. Die Klonmethode kann diese Funktionalität nicht bieten, ist jedoch mit einem Konvertierungskonstruktor einfach: new TreeSet(s).

128
Rose Perrone

Java 8 bietet eine neue Möglichkeit, den Copy-Konstruktor oder die Klonmethode elegant und kompakt für die Element-Hunde aufzurufen: Streams, lambdas und collectors .

Copy-Konstruktor:

List<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toList());

Der Ausdruck Dog::new wird als Methodenreferenz bezeichnet. Es erstellt ein Funktionsobjekt, das einen Konstruktor für Dog aufruft, das einen anderen Hund als Argument akzeptiert.

Klonmethode [1]:

List<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toList());

Als Ergebnis eine ArrayList erhalten

Oder wenn Sie eine ArrayList zurückbekommen müssen (falls Sie sie später ändern möchten):

ArrayList<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toCollection(ArrayList::new));

Aktualisieren Sie die Liste

Wenn Sie den ursprünglichen Inhalt der dogs-Liste nicht aufbewahren müssen, können Sie stattdessen die replaceAll-Methode verwenden und die Liste aktualisieren.

dogs.replaceAll(Dog::new);

Alle Beispiele setzen import static Java.util.stream.Collectors.*; voraus.


Collector für ArrayLists

Der Kollektor aus dem letzten Beispiel kann in eine util-Methode umgewandelt werden. Da dies eine sehr gewöhnliche Sache ist, mag ich es persönlich, kurz und hübsch zu sein. So was:

ArrayList<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toArrayList());

public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
    return Collectors.toCollection(ArrayList::new);
}

[1] Anmerkung zu CloneNotSupportedException:

Damit diese Lösung mit der clone-Methode von Dogdarf nichtfunktioniert, _ deklarieren, dass CloneNotSupportedException ausgelöst wird Der Grund ist, dass das Argument für map keine geprüften Ausnahmen auslösen darf.

So was:

    // Note: Method is public and returns Dog, not Object
    @Override
    public Dog clone() /* Note: No throws clause here */ { ...

Dies sollte jedoch kein großes Problem sein, da dies ohnehin die beste Vorgehensweise ist. (Effectice Java gibt beispielsweise diesen Hinweis.)

Danke an Gustavo, dass Sie dies bemerkt haben.


PS:

Wenn Sie es schöner finden, können Sie stattdessen die Methodenreferenzsyntax verwenden, um genau dasselbe zu tun:

List<Dog> clonedDogs = dogs.stream().map(Dog::clone).collect(toList());
35
Lii

Grundsätzlich gibt es drei Möglichkeiten, ohne manuell zu iterieren, 

1 Konstruktor verwenden

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>(dogs);

2 Verwenden von addAll(Collection<? extends E> c)

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(dogs);

3 Verwenden der addAll(int index, Collection<? extends E> c)-Methode mit dem int-Parameter

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(0, dogs);

Hinweis: Das Verhalten dieser Vorgänge ist undefiniert, wenn die angegebene Auflistung während des Vorgangs geändert wird.

28
javatar

Ich denke, die aktuelle grüne Antwort ist schlecht, warum fragen Sie vielleicht?

  • Es kann erforderlich sein, viel Code hinzuzufügen
  • Sie müssen alle Listen auflisten, die kopiert werden sollen, und dies tun

Die Art und Weise, wie die Serialisierung auch schlecht ist, müssen Sie möglicherweise Serializable überall hinzufügen.

Was ist die Lösung:

Java-Deep-Cloning-Bibliothek Die Cloning-Bibliothek ist eine kleine Open Source-Bibliothek (Apache-Lizenz), die Deep-Clone-Objekte enthält. Die Objekte müssen die Cloneable-Schnittstelle nicht implementieren. In der Tat kann diese Bibliothek JEDE Java-Objekte klonen. Sie kann in Cache-Implementierungen verwendet werden, wenn das zwischengespeicherte Objekt nicht geändert werden soll oder wenn Sie eine tiefe Kopie von Objekten erstellen möchten. 

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

Überprüfen Sie es unter https://github.com/kostaskougios/cloning

18
Cojones

Sie müssen die ArrayList von Hand klonen (indem Sie iterieren und jedes Element in eine neue ArrayList kopieren), da clone() dies nicht für Sie erledigt. Grund dafür ist, dass die in ArrayList enthaltenen Objekte Clonable nicht selbst implementieren können.

Edit : ... und genau das macht der Code von Varkhan.

2
Stephan202

Ich habe einen Weg gefunden, Sie können json verwenden, um die Liste zu serialisieren/unserialisieren. Die serialisierte Liste enthält keinen Verweis auf das ursprüngliche Objekt, wenn es desialisiert wird.

Mit gson:

List<CategoryModel> originalList = new ArrayList<>(); // add some items later
String listAsJson = gson.toJson(originalList);
List<CategoryModel> newList = new Gson().fromJson(listAsJson, new TypeToken<List<CategoryModel>>() {}.getType());

Sie können das auch mit Jackson und jeder anderen Json-Bibliothek tun.

2
sagits

Ein böser Weg ist es mit Reflektion zu tun. So etwas hat bei mir funktioniert.

public static <T extends Cloneable> List<T> deepCloneList(List<T> original) {
    if (original == null || original.size() < 1) {
        return new ArrayList<>();
    }

    try {
        int originalSize = original.size();
        Method cloneMethod = original.get(0).getClass().getDeclaredMethod("clone");
        List<T> clonedList = new ArrayList<>();

        // noinspection ForLoopReplaceableByForEach
        for (int i = 0; i < originalSize; i++) {
            // noinspection unchecked
            clonedList.add((T) cloneMethod.invoke(original.get(i)));
        }
        return clonedList;
    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
        System.err.println("Couldn't clone list due to " + e.getMessage());
        return new ArrayList<>();
    }
}
1
milosmns

Das Paket import org.Apache.commons.lang.SerializationUtils;

Es gibt eine Methode SerializationUtils.clone(Object);

Beispiel

this.myObjectCloned = SerializationUtils.clone(this.object);
0
pacheco

Die anderen Poster sind korrekt: Sie müssen die Liste durchlaufen und in eine neue Liste kopieren.

Wenn jedoch die Objekte in der Liste unveränderlich sind, müssen Sie sie nicht klonen. Wenn Ihr Objekt ein komplexes Objektdiagramm hat, muss es auch unveränderlich sein. 

Ein weiterer Vorteil der Unveränderlichkeit besteht darin, dass sie auch fadensicher sind.

0
Fortyrunner

Einfache Möglichkeit, indem Sie die Bibliothek von Java mit commons-lang-2.3.jar zum Klonen von Listen verwenden

link commons-lang-2.3.jar herunterladen

Wie benutzt man

oldList.........
List<YourObject> newList = new ArrayList<YourObject>();
foreach(YourObject obj : oldList){
   newList.add((YourObject)SerializationUtils.clone(obj));
}

Ich hoffe das kann hilfreich sein.

: D

0
sonida

Ich habe gerade eine Bibliothek entwickelt, die ein Entitätsobjekt und ein Java.util.List-Objekt klonen kann. Laden Sie einfach die JAR-Datei in https://drive.google.com/open?id=0B69Sui5ah93EUTloSktFUkctN0U herunter und verwenden Sie die statische Methode cloneListObject (list). Diese Methode kopiert nicht nur die Liste, sondern auch alle Entitätselemente.

0
Eduardo de Melo

Hier ist eine Lösung, die einen generischen Vorlagentyp verwendet:

public static <T> List<T> copyList(List<T> source) {
    List<T> dest = new ArrayList<T>();
    for (T item : source) { dest.add(item); }
    return dest;
}
0
Andrew Coyte

Ich habe diese Option immer benutzt: 

ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone);
0
besartm

für Sie überschreiben Sie die clone () -Methode

class You_class {

    int a;

    @Override
    public You_class clone() {
        You_class you_class = new You_class();
        you_class.a = this.a;
        return you_class;
    }
}

und rufen Sie .clone () für Vector obj oder ArraiList obj .... auf.

0
RN3KK Nick