Deep Clone Utility-Empfehlung [geschlossen]

74

Gibt es ein Dienstprogramm zum tiefen Klonen für Java-Sammlungen:

  • Arrays
  • Listen
  • Karten

HINWEIS: Bevorzugen Sie eine Lösung ohne Verwendung der Serialisierung, jedoch unter Verwendung der Object.clone () -Methode. Ich kann sicher sein, dass mein benutzerdefiniertes Objekt die Methode clone () implementiert und nur Java-Standardklassen verwendet, die klonbar sind ...

Juraj
quelle
Die Verwendung der Klonbibliothek hat mir den Tag gerettet! github.com/kostaskougios/cloning
Gaurav

Antworten:

65

Ich denke, die vorherige grüne Antwort war schlecht , warum könnten Sie fragen?

  • Es fügt viel Code hinzu
  • Dazu müssen Sie alle zu kopierenden Felder auflisten und dies tun
  • Dies funktioniert nicht für Listen, wenn clone () verwendet wird (Dies ist, was clone () für HashMap sagt: Gibt eine flache Kopie dieser HashMap-Instanz zurück: Die Schlüssel und Werte selbst werden nicht geklont.), So dass Sie dies am Ende manuell tun (dies macht ich weine)

Oh, und übrigens ist die Serialisierung auch schlecht. Möglicherweise müssen Sie überall Serializable hinzufügen (das bringt mich auch zum Weinen).

Was ist die Lösung:

Java Deep-Cloning-Bibliothek Die Cloning-Bibliothek ist eine kleine Open-Source-Java-Bibliothek (Apache-Lizenz), die Objekte tief klont. Die Objekte müssen die klonbare Schnittstelle nicht implementieren. Tatsächlich kann diese Bibliothek JEDE Java-Objekte klonen. Es kann verwendet werden, dh in Cache-Implementierungen, wenn Sie nicht möchten, dass das zwischengespeicherte Objekt geändert wird, 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

Cojones
quelle
4
Cloner ist eine großartige Bibliothek (nur ihre Leistung bringt mich manchmal zum Weinen ... aber ich denke, man kann mit Reflexion nicht viel besser
umgehen
Diese Lösung sieht besser aus. Ich habe es nicht versucht, aber es gibt einige Kommentare in diesem Thread, die sehr positiv über Cloner sind.
Juraj
1
Funktioniert nicht auf Android ...
wieczorek1990
Was war die vorherige grüne Antwort?
DaveFar
1
Es gibt ein großes Problem mit dieser Bibliothek. Wenn Sie deppClone () einfach so verwenden, wird zu viel kopiert !!! Was ich damit meine ist, wenn Sie eine "Aufzählung" haben, die eine "abstrakte" Methode in ihrem Körper deklariert und jede Konstante zwingt, eine andere Implementierung für diese abstrakte Methode zu geben, nachdem Sie ein Feld aus dieser Aufzählung geklont haben, "==" funktioniert nicht mehr für diese Konstanten !! Was leicht zu Problemen führen kann ... Es gibt einige Lösungen in der Bibliothek. registerFastCloner () und registerImmutable () könnten beide eine Lösung sein, haben sie noch nicht ausprobiert ... Und ich weiß nicht, ob es überhaupt gelöst werden kann!
Mostafa Zeinali
20

Alle Ansätze zum Kopieren von Objekten in Java weisen schwerwiegende Mängel auf:

Klon

  1. Die clone () -Methode ist geschützt, sodass Sie sie nur dann direkt aufrufen können, wenn die betreffende Klasse sie mit einer öffentlichen Methode überschreibt.
  2. clone () ruft den Konstruktor nicht auf. Jeder Konstruktor. Es wird Speicher zuweisen, das interne classFeld zuweisen (über das Sie lesen können getClass()) und die Felder des Originals kopieren.

Weitere Probleme mit clone () finden Sie in Punkt 11 von Joshua Blochs Buch " Effective Java, Second Edition ".

Serialisieren

Serialisieren ist noch schlimmer; es hat viele der Mängel von clone()und noch einige mehr. Joshua hat ein ganzes Kapitel mit vier Punkten allein für dieses Thema.

Meine Lösung

Meine Lösung besteht darin, meinen Projekten eine neue Oberfläche hinzuzufügen:

public interface Copyable<T> {
    T copy ();
    T createForCopy ();
    void copyTo (T dest);
}

Der Code sieht folgendermaßen aus:

class Demo implements Copyable<Demo> {
    public Demo copy () {
        Demo copy = createForCopy ();
        copyTo (copy);
        return copy;
    }
    public Demo createForCopy () {
        return new Demo ();
    }
    public void copyTo (Demo dest)
        super.copyTo (dest);
        ...copy fields of Demo here...
    }
}

Leider muss ich diesen Code in alle meine Objekte kopieren, aber es ist immer der gleiche Code, sodass ich eine Eclipse-Editor-Vorlage verwenden kann. Vorteile:

  1. Ich kann entscheiden, welcher Konstruktor aufgerufen werden soll und wie welches Feld initialisiert werden soll.
  2. Die Initialisierung erfolgt in einer deterministischen Reihenfolge (Stammklasse zu Instanzklasse).
  3. Ich kann vorhandene Objekte wiederverwenden und überschreiben
  4. Geben Sie safe ein
  5. Singletons bleiben Singletons

Für Standard-Java-Typen (wie Sammlungen usw.) verwende ich eine Dienstprogrammklasse, die diese kopieren kann. Die Methoden haben Flags und Callbacks, sodass ich steuern kann, wie tief eine Kopie sein soll.

Aaron Digulla
quelle
2
Ich habe etwas Ähnliches getan, indem ich clone () in allen Klassen implementiert habe, die ich klonen muss. Das größte Problem ist, dass ich, wenn ich eine Sammlung habe, diese durchlaufen und selbst kopieren muss ...
Juraj
Verwenden Sie eine Hilfsfunktion, die eine Sammlung akzeptiert und eine ArrayList zurückgibt: Da Sie die Größe kennen, wird der Speicher nur einmal zugewiesen, und ArrayList ist für die üblichen Zugriffsarten schnell.
Aaron Digulla
createForCopy muss eine Demo zurückgeben
TimP
Möchten Sie die Eclipse-Editor-Vorlage anzeigen?
Michal
17

Das flache Klonen einer Sammlung ist einfach, aber wenn Sie tief klonen möchten, ist eine Bibliothek wahrscheinlich besser als das manuelle Codieren (da Sie auch die Elemente in der Sammlung klonen möchten ).

Genau wie diese Antwort habe ich die Cloner-Bibliothek verwendet und die Leistung speziell gegen XStream (das durch Serialisieren und Deserialisieren "klonen" kann) und binäre Serialisierung getestet. Obwohl XStream sehr schnell in / aus XML serialisiert, ist Cloner beim Klonen viel schneller:

0,0851 ms: xstream (Klonen durch Serialisieren / Deserialisieren)
0,0223 ms: Binäre Serialisierung (Klonen durch Serialisieren / Deserialisieren)
0,0017 ms: Kloner
* durchschnittliche Zeit zum Klonen eines einfachen Objekts (zwei Felder) und kein öffentlicher Standardkonstruktor. 10.000 Mal ausführen.

Neben der Schnelligkeit gibt es noch weitere Gründe, sich für Cloner zu entscheiden:

  1. führt einen tiefen Klon eines Objekts durch (auch derjenigen, die Sie nicht selbst schreiben)
  2. Sie müssen Ihre clone () -Methode nicht jedes Mal auf dem neuesten Stand halten, wenn Sie ein Feld hinzufügen
  3. Sie können Objekte klonen, die keinen öffentlichen Standardkonstruktor haben
  4. arbeitet mit Frühling
  5. (Optimierung) klont keine bekannten unveränderlichen Objekte (wie Integer, String usw.)
  6. Einfach zu verwenden. Beispiel:

    cloner.deepClone (anyObject);

Brad Cupit
quelle
14

Ich bin der Schöpfer der Cloner Lib, die Brad vorgestellt hat. Dies ist eine Lösung zum Klonen von Objekten, ohne dass zusätzlicher Code geschrieben werden muss (keine serialisierbaren Objekte oder impl clone () -Methode erforderlich).

Es ist ziemlich schnell, wie Brad sagte, und kürzlich habe ich eine Version hochgeladen, die noch schneller ist. Beachten Sie, dass die manuelle Implementierung einer clone () -Methode schneller ist als die von clone lib, aber andererseits müssen Sie viel Code schreiben.

Cloner lib hat für mich sehr gut funktioniert, da ich es in einer Cache-Implementierung für eine Site mit sehr viel Verkehr (~ 1 Million Anfragen / Tag) verwende. Der Cache sollte ungefähr 10 Objekte pro Anforderung klonen. Es ist sehr zuverlässig und stabil. Beachten Sie jedoch, dass das Klonen nicht ohne Risiko ist. Die Bibliothek kann so konfiguriert werden, dass sie jede Klasseninstanz druckt, die sie während der Entwicklung klont. Auf diese Weise können Sie überprüfen, ob es klont, was Ihrer Meinung nach geklont werden sollte. Objektdiagramme können sehr tief sein und Verweise auf eine überraschend große Anzahl von Objekten enthalten. Mit clone lib können Sie anweisen, die nicht gewünschten Objekte, dh Singletons, nicht zu klonen.

Konstantinos Kougios
quelle
Die Bibliothek sieht gut aus, wird es einige Zeit später überprüfen ...
Juraj
"Mit clone lib können Sie es anweisen, die Objekte, die Sie nicht möchten, nicht zu klonen." Ich habe dies versucht, konnte aber nicht, wie kann ich es bitten, bestimmte Felder nicht zu klonen?
Sudarshan
Grüße Konstantinos. Ich verwende Ihre Bibliothek anstelle der Apache-Utils (hatte Probleme mit Casts mit SerializableUtils). Ihre arbeitet ohne Probleme. Gute Arbeit!.
Will824
Ich benutze ur lib schon lange, es ist wirklich toll! tks ur! D:
Adams.H
10

Eine allgemeine Möglichkeit, eine beliebige Sammlung tief zu klonen, besteht darin, sie in einen Stream zu serialisieren und dann in eine neue Sammlung zurückzulesen. Sie werden völlig neue Objekte rehydrieren, die keine Beziehung zu den alten haben, außer identische Kopien zu sein.

In Brunos Antwort finden Sie einen Link zu den Apache Commons-Dienstprogrammklassen für die Serialisierung. Dies ist sehr hilfreich, wenn Sie sich für diesen Weg entscheiden.

John Feminella
quelle
Die Serialisierungslösung ist in Ordnung, aber ich habe über etwas ohne nachgedacht. Ich kann garantieren, dass mein benutzerdefiniertes Objekt mit der clone () -Methode korrekt tief geklont wird, aber ich möchte einen Helfer, der dies für Standard-Java-Klassen erledigt ...
Juraj
Serialisierung Weg zum Klonen ist in Ordnung, aber ich hatte einige Felder, die nicht unter meiner Kontrolle sind und die nicht serialisierbar sind ...
Juraj
5

Eine Möglichkeit ist die Verwendung der Serialisierung :

Apache Commons bietet SerializationUtils

bruno conde
quelle
2

Ich habe diese Klonbibliothek verwendet und fand sie sehr nützlich. Da es einige Einschränkungen gab (ich brauchte eine genauere Kontrolle über den Klonprozess: Welches Feld, in welchem ​​Kontext und wie tief sollte geklont werden usw.), habe ich eine erweiterte Version davon erstellt. Sie steuern das Klonen der Felder, indem Sie sie in der Entitätsklasse mit Anmerkungen versehen.

Um einen Eindruck davon zu bekommen, hier eine Beispielklasse:

public class CloneMePlease {
    @Clone(Skip.class)
    String id3 = UUID.randomUUID().toString();

    @Clone(Null.class)
    String id4 = UUID.randomUUID().toString();

    @Clone(value = RandomUUID.class, groups=CustomActivationGroup1.class)
    String id5 = UUID.randomUUID().toString();

    @Clone.List({
            @Clone(groups=CustomActivationGroup2.class, value=Skip.class),
            @Clone(groups=CustomActivationGroup3.class, value=Copy.class)})
    Object activationGroupOrderTest = new Object();

    @Clone(LongIncrement.class)
    long version = 1l;

    @PostClone
    private void postClone(CloneMePlease original, @CloneInject CloneInjectedService service){
         //do stuff with the original source object in the context of the cloned object
         //you can inject whatewer service you want, from spring/guice to perform custom logic here
    }
}

Weitere Details hier: https://github.com/mnorbi/fluidity-cloning

Es gibt auch eine hibernate-spezifische Erweiterung, falls diese benötigt wird.

Norbert Madarász
quelle
0

Verwenden Sie die Serialisierung und dann die Deserialisierung. Beachten Sie jedoch, dass dieser Ansatz nur mit serialisierbaren Klassen ohne transiente Felder funktioniert. Außerdem werden Ihre Singletons keine Singletons mehr sein.

Marko
quelle
Die Verwendung von Serialisierung und Deserialisierung für das Klonen von Speicherobjekten zur Laufzeit oder zwischen separaten Programmläufen ist eine schlechte Idee. Weitere Informationen dazu finden Sie unter Google: "Warum Serialisierung und Deserialisierung schlecht sind".
Eric Leschinski