Es ist etwas schwierig, eine Deep Object Copy-Funktion zu implementieren. Welche Schritte unternehmen Sie, um sicherzustellen, dass das ursprüngliche und das geklonte Objekt keinen Bezug haben?
Kryo verfügt über eine integrierte Unterstützung für das Kopieren / Klonen . Dies ist direktes Kopieren von Objekt zu Objekt, nicht Objekt-> Bytes-> Objekt.
Vorsichtsmaßnahmen: Klassen können die Serialisierung überschreiben, sodass keine neuen Instanzen erstellt werden, z. B. für Singletons. Auch dies funktioniert natürlich nicht, wenn Ihre Klassen nicht serialisierbar sind.
Beachten Sie, dass die im Artikel bereitgestellte FastByteArrayOutputStream-Implementierung effizienter sein kann. Es wird eine Erweiterung im ArrayList-Stil verwendet, wenn der Puffer voll ist. Es ist jedoch besser, einen Erweiterungsansatz im LinkedList-Stil zu verwenden. Anstatt einen neuen 2x-Puffer zu erstellen und den aktuellen Puffer zu speichern, pflegen Sie eine verknüpfte Liste von Puffern und fügen Sie einen neuen hinzu, wenn der aktuelle Puffer voll ist. Wenn Sie die Anforderung erhalten, mehr Daten zu schreiben, als in Ihre Standardpuffergröße passen, erstellen Sie einen Pufferknoten, der genau so groß ist wie die Anforderung. Die Knoten müssen nicht gleich groß sein.
Die verknüpfte @ BrianHarris-Liste ist nicht effizienter als ein dynamisches Array. Das Einfügen von Elementen in ein dynamisches Array wird mit konstanter Komplexität abgeschrieben, während das Einfügen in eine verknüpfte Liste eine lineare Komplexität darstellt
Norill Tempest,
Wie viel serialisieren und deserialisieren Sie langsamer als beim Kopierkonstruktor?
Woland
75
Einige Leute haben die Verwendung oder Überschreibung erwähnt Object.clone(). Tu es nicht. Object.clone()hat einige große Probleme, und von seiner Verwendung wird in den meisten Fällen abgeraten. Eine vollständige Antwort finden Sie unter Punkt 11 aus " Effective Java " von Joshua Bloch. Ich glaube, Sie können sicher Object.clone()auf Arrays vom primitiven Typ verwenden, aber abgesehen davon müssen Sie vorsichtig sein, wenn Sie den Klon richtig verwenden und überschreiben.
Die Schemata, die auf Serialisierung (XML oder auf andere Weise) beruhen, sind klobig.
Hier gibt es keine einfache Antwort. Wenn Sie ein Objekt tief kopieren möchten, müssen Sie das Objektdiagramm durchlaufen und jedes untergeordnete Objekt explizit über den Kopierkonstruktor des Objekts oder eine statische Factory-Methode kopieren, die wiederum das untergeordnete Objekt tief kopiert. Unveränderliche (z. B. String) müssen nicht kopiert werden. Nebenbei sollten Sie aus diesem Grund die Unveränderlichkeit bevorzugen.
Sie können eine tiefe Kopie mit Serialisierung erstellen, ohne Dateien zu erstellen.
Ihr Objekt, das Sie tief kopieren möchten, muss implement serializable . Wenn die Klasse nicht endgültig ist oder nicht geändert werden kann, erweitern Sie die Klasse und implementieren Sie serialisierbar.
Konvertieren Sie Ihre Klasse in einen Stream von Bytes:
Es ist auch verfügbar inorg.apache.commons.lang.SerializationUtils
Pino
25
Eine Möglichkeit, Deep Copy zu implementieren, besteht darin, jeder zugeordneten Klasse Kopierkonstruktoren hinzuzufügen. Ein Kopierkonstruktor verwendet eine Instanz von 'this' als einzelnes Argument und kopiert alle Werte daraus. Ziemlich viel Arbeit, aber ziemlich unkompliziert und sicher.
BEARBEITEN: Beachten Sie, dass Sie zum Lesen von Feldern keine Zugriffsmethoden verwenden müssen. Sie können direkt auf alle Felder zugreifen, da die Quellinstanz immer vom gleichen Typ ist wie die Instanz mit dem Kopierkonstruktor. Offensichtlich, könnte aber übersehen werden.
Bearbeiten: Beachten Sie, dass Sie bei Verwendung von Kopierkonstruktoren den Laufzeittyp des zu kopierenden Objekts kennen müssen. Mit dem obigen Ansatz können Sie eine gemischte Liste nicht einfach kopieren (möglicherweise können Sie dies mit einem Reflexionscode tun).
Nur interessiert für den Fall, dass das, was Sie kopieren, eine Unterklasse ist, aber vom übergeordneten Element referenziert wird. Ist es möglich, den Kopierkonstruktor zu überschreiben?
Pork 'n' Bunny
Warum bezieht sich Ihre Elternklasse auf ihre Unterklasse? Kannst du ein Beispiel geben?
Adriaan Koster
1
öffentliches Auto erweitert Fahrzeug und bezieht sich dann auf das Auto als Fahrzeug. originaList = new ArrayList <Fahrzeug>; copyList = new ArrayList <Fahrzeug>; originalList.add (neues Auto ()); für (Fahrzeug Fahrzeug: Fahrzeugliste) {copyList.add (neues Fahrzeug (Fahrzeug)); }
Pork 'n' Bunny
@AdriaanKoster: Wenn die ursprüngliche Liste ein enthält Toyota, wird Ihr Code ein Carin die Zielliste einfügen . Für ein ordnungsgemäßes Klonen muss die Klasse im Allgemeinen eine virtuelle Factory-Methode bereitstellen, deren Vertrag besagt, dass sie ein neues Objekt ihrer eigenen Klasse zurückgibt. Der Kopierkonstruktor selbst sollte protectedsicherstellen, dass er nur zum Erstellen von Objekten verwendet wird, deren genauer Typ mit dem des zu kopierenden Objekts übereinstimmt.
Supercat
Wenn ich Ihren Vorschlag richtig verstehe, würde die Factory-Methode den Konstruktor für private Kopien aufrufen? Wie würde der Kopierkonstruktor einer Unterklasse sicherstellen, dass die Felder der Oberklasse initialisiert werden? Kannst du ein Beispiel geben?
Adriaan Koster
20
Sie können eine Bibliothek verwenden , die über eine einfache API verfügt und ein relativ schnelles Klonen mit Reflektion durchführt (sollte schneller sein als Serialisierungsmethoden).
Cloner cloner =newCloner();MyClass clone = cloner.deepClone(o);// clone is a deep-clone of o
Nein, Sie brauchen nicht den Aufwand für die XML-Verknüpfung des Objekts.
Egelev
@egeleve Du merkst doch, dass du auf einen Kommentar von '08 antwortest, oder? Ich benutze kein Java mehr und es gibt jetzt wahrscheinlich bessere Tools. Zu dieser Zeit schien es jedoch ein guter Hack zu sein, in ein anderes Format zu serialisieren und dann zurück zu serialisieren - es war definitiv ineffizient.
Sankara
10
Ein sehr einfacher und einfacher Ansatz besteht darin, Jackson JSON zu verwenden, um komplexe Java-Objekte in JSON zu serialisieren und zurückzulesen.
Für Spring Framework- Benutzer. Klasse verwenden org.springframework.util.SerializationUtils:
@SuppressWarnings("unchecked")publicstatic<T extendsSerializable> T clone(T object){return(T)SerializationUtils.deserialize(SerializationUtils.serialize(object));}
Für komplizierte Objekte und wenn die Leistung nicht signifikant ist, verwende ich eine JSON-Bibliothek wie Gson
um das Objekt in JSON-Text zu serialisieren, und deserialisiere dann den Text, um ein neues Objekt zu erhalten.
gson, das auf Reflexion basiert, funktioniert in den meisten Fällen, außer dass transientFelder nicht kopiert werden und Objekte mit Zirkelverweis mit Ursache StackOverflowError.
publicstatic<T> T copy(T anObject,Class<T> classInfo){Gson gson =newGsonBuilder().create();String text = gson.toJson(anObject);
T newObject = gson.fromJson(text, classInfo);return newObject;}publicstaticvoid main(String[] args){String originalObject ="hello";String copiedObject = copy(originalObject,String.class);}
Bitte halten Sie sich für sich und für uns an die Java-Namenskonventionen.
Patrick Bergner
8
Verwenden Sie XStream ( http://x-stream.github.io/ ). Sie können sogar steuern, welche Eigenschaften Sie durch Anmerkungen ignorieren oder den Eigenschaftsnamen explizit für die XStream-Klasse angeben können. Darüber hinaus müssen Sie keine klonbare Schnittstelle implementieren.
Tiefes Kopieren kann nur mit Zustimmung jeder Klasse durchgeführt werden. Wenn Sie die Kontrolle über die Klassenhierarchie haben, können Sie die klonbare Schnittstelle und die Clone-Methode implementieren. Andernfalls ist eine sichere Kopie nicht sicher durchzuführen, da das Objekt möglicherweise auch Nicht-Datenressourcen (z. B. Datenbankverbindungen) gemeinsam nutzt. Im Allgemeinen wird jedoch tiefes Kopieren in der Java-Umgebung als schlechte Praxis angesehen und sollte über die entsprechenden Entwurfspraktiken vermieden werden.
Verwenden von Jackson zum Serialisieren und Deserialisieren des Objekts. Für diese Implementierung muss das Objekt die Serializable-Klasse nicht implementieren.
Antworten:
Ein sicherer Weg besteht darin, das Objekt zu serialisieren und dann zu deserialisieren. Dies stellt sicher, dass alles eine brandneue Referenz ist.
Hier ist ein Artikel darüber, wie dies effizient durchgeführt werden kann.
Vorsichtsmaßnahmen: Klassen können die Serialisierung überschreiben, sodass keine neuen Instanzen erstellt werden, z. B. für Singletons. Auch dies funktioniert natürlich nicht, wenn Ihre Klassen nicht serialisierbar sind.
quelle
Einige Leute haben die Verwendung oder Überschreibung erwähnt
Object.clone()
. Tu es nicht.Object.clone()
hat einige große Probleme, und von seiner Verwendung wird in den meisten Fällen abgeraten. Eine vollständige Antwort finden Sie unter Punkt 11 aus " Effective Java " von Joshua Bloch. Ich glaube, Sie können sicherObject.clone()
auf Arrays vom primitiven Typ verwenden, aber abgesehen davon müssen Sie vorsichtig sein, wenn Sie den Klon richtig verwenden und überschreiben.Die Schemata, die auf Serialisierung (XML oder auf andere Weise) beruhen, sind klobig.
Hier gibt es keine einfache Antwort. Wenn Sie ein Objekt tief kopieren möchten, müssen Sie das Objektdiagramm durchlaufen und jedes untergeordnete Objekt explizit über den Kopierkonstruktor des Objekts oder eine statische Factory-Methode kopieren, die wiederum das untergeordnete Objekt tief kopiert. Unveränderliche (z. B.
String
) müssen nicht kopiert werden. Nebenbei sollten Sie aus diesem Grund die Unveränderlichkeit bevorzugen.quelle
Sie können eine tiefe Kopie mit Serialisierung erstellen, ohne Dateien zu erstellen.
Ihr Objekt, das Sie tief kopieren möchten, muss
implement serializable
. Wenn die Klasse nicht endgültig ist oder nicht geändert werden kann, erweitern Sie die Klasse und implementieren Sie serialisierbar.Konvertieren Sie Ihre Klasse in einen Stream von Bytes:
Stellen Sie Ihre Klasse aus einem Bytestrom wieder her:
quelle
instance
in diesem Fall nicht serialisierbar ?Sie können
org.apache.commons.lang3.SerializationUtils.clone(T)
in Apache Commons Lang einen auf Serialisierung basierenden Deep Clone erstellen, aber seien Sie vorsichtig - die Leistung ist miserabel.Im Allgemeinen empfiehlt es sich, für jede Klasse eines Objekts im zu klonenden Objektdiagramm eigene Klonmethoden zu schreiben.
quelle
org.apache.commons.lang.SerializationUtils
Eine Möglichkeit, Deep Copy zu implementieren, besteht darin, jeder zugeordneten Klasse Kopierkonstruktoren hinzuzufügen. Ein Kopierkonstruktor verwendet eine Instanz von 'this' als einzelnes Argument und kopiert alle Werte daraus. Ziemlich viel Arbeit, aber ziemlich unkompliziert und sicher.
BEARBEITEN: Beachten Sie, dass Sie zum Lesen von Feldern keine Zugriffsmethoden verwenden müssen. Sie können direkt auf alle Felder zugreifen, da die Quellinstanz immer vom gleichen Typ ist wie die Instanz mit dem Kopierkonstruktor. Offensichtlich, könnte aber übersehen werden.
Beispiel:
Bearbeiten: Beachten Sie, dass Sie bei Verwendung von Kopierkonstruktoren den Laufzeittyp des zu kopierenden Objekts kennen müssen. Mit dem obigen Ansatz können Sie eine gemischte Liste nicht einfach kopieren (möglicherweise können Sie dies mit einem Reflexionscode tun).
quelle
Toyota
, wird Ihr Code einCar
in die Zielliste einfügen . Für ein ordnungsgemäßes Klonen muss die Klasse im Allgemeinen eine virtuelle Factory-Methode bereitstellen, deren Vertrag besagt, dass sie ein neues Objekt ihrer eigenen Klasse zurückgibt. Der Kopierkonstruktor selbst sollteprotected
sicherstellen, dass er nur zum Erstellen von Objekten verwendet wird, deren genauer Typ mit dem des zu kopierenden Objekts übereinstimmt.Sie können eine Bibliothek verwenden , die über eine einfache API verfügt und ein relativ schnelles Klonen mit Reflektion durchführt (sollte schneller sein als Serialisierungsmethoden).
quelle
Apache Commons bietet eine schnelle Möglichkeit, ein Objekt tief zu klonen.
quelle
XStream ist in solchen Fällen sehr nützlich. Hier ist ein einfacher Code zum Klonen
quelle
Ein sehr einfacher und einfacher Ansatz besteht darin, Jackson JSON zu verwenden, um komplexe Java-Objekte in JSON zu serialisieren und zurückzulesen.
http://wiki.fasterxml.com/JacksonInFiveMinutes
quelle
Für Spring Framework- Benutzer. Klasse verwenden
org.springframework.util.SerializationUtils
:quelle
Für komplizierte Objekte und wenn die Leistung nicht signifikant ist, verwende ich eine JSON-Bibliothek wie Gson um das Objekt in JSON-Text zu serialisieren, und deserialisiere dann den Text, um ein neues Objekt zu erhalten.
gson, das auf Reflexion basiert, funktioniert in den meisten Fällen, außer dass
transient
Felder nicht kopiert werden und Objekte mit Zirkelverweis mit UrsacheStackOverflowError
.quelle
Verwenden Sie XStream ( http://x-stream.github.io/ ). Sie können sogar steuern, welche Eigenschaften Sie durch Anmerkungen ignorieren oder den Eigenschaftsnamen explizit für die XStream-Klasse angeben können. Darüber hinaus müssen Sie keine klonbare Schnittstelle implementieren.
quelle
Tiefes Kopieren kann nur mit Zustimmung jeder Klasse durchgeführt werden. Wenn Sie die Kontrolle über die Klassenhierarchie haben, können Sie die klonbare Schnittstelle und die Clone-Methode implementieren. Andernfalls ist eine sichere Kopie nicht sicher durchzuführen, da das Objekt möglicherweise auch Nicht-Datenressourcen (z. B. Datenbankverbindungen) gemeinsam nutzt. Im Allgemeinen wird jedoch tiefes Kopieren in der Java-Umgebung als schlechte Praxis angesehen und sollte über die entsprechenden Entwurfspraktiken vermieden werden.
quelle
quelle
Ich habe Dozer zum Klonen von Java-Objekten verwendet und es ist großartig, dass die Kryo- Bibliothek eine weitere großartige Alternative ist.
quelle
BeanUtils macht einen wirklich guten Job beim tiefen Klonen von Bohnen.
quelle
1)
Hier müssen Ihre MyPerson- und MyAddress-Klassen eine serilazierbare Schnittstelle implementieren
quelle
Verwenden von Jackson zum Serialisieren und Deserialisieren des Objekts. Für diese Implementierung muss das Objekt die Serializable-Klasse nicht implementieren.
quelle