Il est un peu difficile d'implémenter une fonction de copie d'objet approfondie. Quelles mesures prenez-vous pour vous assurer que l'objet d'origine et celui cloné ne partagent aucune référence?
Avertissements: Il est possible pour les classes de remplacer la sérialisation de sorte que de nouvelles instances ne soient pas créées, par exemple pour les singletons. Bien sûr, cela ne fonctionne pas si vos classes ne sont pas sérialisables.
Sachez que l'implémentation FastByteArrayOutputStream fournie dans l'article pourrait être plus efficace. Il utilise une expansion de style ArrayList lorsque le tampon se remplit, mais il est préférable d'utiliser une approche d'expansion de style LinkedList. Au lieu de créer un nouveau tampon 2x et de mémoriser le tampon actuel, conservez une liste liée de tampons, en ajoutant un nouveau lorsque le courant se remplit. Si vous obtenez une demande pour écrire plus de données que ne le permettrait votre taille de mémoire tampon par défaut, créez un nœud de tampon qui est exactement aussi grand que la demande; les nœuds n'ont pas besoin d'être de la même taille.
La liste chaînée @BrianHarris n'est pas plus efficace qu'un tableau dynamique. L'insertion d'éléments dans un tableau dynamique est une complexité constante amortie, tandis que l'insertion dans une liste chaînée est une complexité linéaire
Norill Tempest
Combien sérialiser et désérialiser plus lentement que l'approche du constructeur de copie?
Woland
75
Quelques personnes ont mentionné l'utilisation ou le dépassement Object.clone(). Ne le fais pas. Object.clone()présente quelques problèmes majeurs et son utilisation est déconseillée dans la plupart des cas. Veuillez consulter le point 11 de « Effective Java » de Joshua Bloch pour une réponse complète. Je crois que vous pouvez utiliser en toute sécurité Object.clone()sur des tableaux de type primitif, mais à part cela, vous devez être judicieux sur l'utilisation correcte et la substitution du clone.
Les schémas qui reposent sur la sérialisation (XML ou autre) sont délicats.
Il n'y a pas de réponse facile ici. Si vous souhaitez copier en profondeur un objet, vous devrez parcourir le graphique d'objet et copier chaque objet enfant de manière explicite via le constructeur de copie de l'objet ou une méthode d'usine statique qui à son tour copie en profondeur l'objet enfant. Les objets immuables (par exemple les Strings) n'ont pas besoin d'être copiés. En passant, vous devriez privilégier l'immuabilité pour cette raison.
Vous pouvez faire une copie complète avec sérialisation sans créer de fichiers.
Votre objet que vous souhaitez copier en profondeur devra implement serializable . Si la classe n'est pas définitive ou ne peut pas être modifiée, étendez la classe et implémentez sérialisable.
Si le cours est final, comment le prolongeriez-vous?
Kumar Manish
1
@KumarManish classe MyContainer implémente Serializable {MyFinalClass instance; ...}
Matteo T.
Je trouve cela une excellente réponse. Clone is a mess
blackbird014
@MatteoT. comment la propriété de classe non sérialisable sera sérialisée, non sérialisable instancedans ce cas?
Farid
40
Vous pouvez faire un clone profond basé sur la sérialisation en utilisant org.apache.commons.lang3.SerializationUtils.clone(T) en dans Apache Commons Lang, mais attention - les performances sont épouvantables.
En général, il est recommandé d'écrire vos propres méthodes de clonage pour chaque classe d'un objet dans le graphe d'objet nécessitant un clonage.
Il est également disponible enorg.apache.commons.lang.SerializationUtils
Pino
25
Une façon d'implémenter la copie approfondie consiste à ajouter des constructeurs de copie à chaque classe associée. Un constructeur de copie prend une instance de «ceci» comme argument unique et en copie toutes les valeurs. Beaucoup de travail, mais assez simple et sûr.
EDIT: notez que vous n'avez pas besoin d'utiliser des méthodes d'accesseur pour lire les champs. Vous pouvez accéder directement à tous les champs car l'instance source est toujours du même type que l'instance avec le constructeur de copie. Évident mais pourrait être négligé.
Modifier: notez que lorsque vous utilisez des constructeurs de copie, vous devez connaître le type d'exécution de l'objet que vous copiez. Avec l'approche ci-dessus, vous ne pouvez pas facilement copier une liste mixte (vous pourrez peut-être le faire avec du code de réflexion).
Je veux juste savoir si ce que vous copiez est une sous-classe, mais est référencé par le parent. Est-il possible de remplacer le constructeur de copie?
Pork 'n' Bunny
Pourquoi votre classe parent fait-elle référence à sa sous-classe? Pouvez-vous donner un exemple?
Adriaan Koster
1
public class Car étend Vehicle Et se réfère ensuite à la voiture en tant que véhicule. originaList = new ArrayList <Vehicle>; copyList = new ArrayList <Vehicle>; originalList.add (nouvelle voiture ()); pour (Véhicule véhicule: véhiculeListe) {copyList.add (nouveau Véhicule (véhicule)); }
Pork 'n' Bunny
@AdriaanKoster: Si la liste d'origine contient un Toyota, votre code mettra un Cardans la liste de destination. Un clonage correct nécessite généralement que la classe fournisse une méthode d'usine virtuelle dont le contrat stipule qu'elle retournera un nouvel objet de sa propre classe; le constructeur de la copie lui-même doit protecteds'assurer qu'il ne sera utilisé que pour construire des objets dont le type précis correspond à celui de l'objet copié).
supercat
Donc, si je comprends bien votre suggestion, la méthode d'usine appellerait le constructeur de copie privée? Comment le constructeur de copie d'une sous-classe s'assurerait-il que les champs de la superclasse sont initialisés? Pouvez-vous donner un exemple?
Adriaan Koster
20
Vous pouvez utiliser une bibliothèque dotée d'une API simple et effectuer un clonage relativement rapide avec réflexion (devrait être plus rapide que les méthodes de sérialisation).
Cloner cloner =newCloner();MyClass clone = cloner.deepClone(o);// clone is a deep-clone of o
Non, vous n'avez pas besoin de la surcharge de xml-ing de l'objet.
egelev
@egeleve Vous vous rendez compte que vous répondez à un commentaire de '08 non? Je n'utilise plus Java et il y a probablement de meilleurs outils maintenant. Cependant, à cette époque, la sérialisation dans un format différent, puis la sérialisation en arrière semblait être un bon hack - c'était définitivement inefficace.
sankara
10
Une approche très facile et simple consiste à utiliser Jackson JSON pour sérialiser un objet Java complexe en JSON et le relire.
Pour les utilisateurs de Spring Framework . Utilisation de la classe org.springframework.util.SerializationUtils:
@SuppressWarnings("unchecked")publicstatic<T extendsSerializable> T clone(T object){return(T)SerializationUtils.deserialize(SerializationUtils.serialize(object));}
Pour les objets compliqués et lorsque les performances ne sont pas importantes, j'utilise une bibliothèque json, comme gson
pour sérialiser l'objet en texte json, puis désérialiser le texte pour obtenir un nouvel objet.
gson qui basé sur la réflexion fonctionne dans la plupart des cas, sauf que les transientchamps ne seront pas copiés et les objets avec référence circulaire avec cause 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);}
Veuillez respecter les conventions de dénomination Java pour vous et pour notre bien.
Patrick Bergner du
8
Utilisez XStream ( http://x-stream.github.io/ ). Vous pouvez même contrôler les propriétés que vous pouvez ignorer via des annotations ou en spécifiant explicitement le nom de la propriété à la classe XStream. De plus, vous n'avez pas besoin d'implémenter une interface clonable.
La copie en profondeur ne peut être effectuée qu'avec le consentement de chaque classe. Si vous contrôlez la hiérarchie des classes, vous pouvez implémenter l'interface clonable et implémenter la méthode Clone. Sinon, il est impossible de faire une copie complète en toute sécurité car l'objet peut également partager des ressources non liées aux données (par exemple, les connexions à la base de données). En général, cependant, la copie en profondeur est considérée comme une mauvaise pratique dans l'environnement Java et doit être évitée via les pratiques de conception appropriées.
Réponses:
Un moyen sûr consiste à sérialiser l'objet, puis à le désérialiser. Cela garantit que tout est une toute nouvelle référence.
Voici un article sur la façon de procéder efficacement.
Avertissements: Il est possible pour les classes de remplacer la sérialisation de sorte que de nouvelles instances ne soient pas créées, par exemple pour les singletons. Bien sûr, cela ne fonctionne pas si vos classes ne sont pas sérialisables.
la source
Quelques personnes ont mentionné l'utilisation ou le dépassement
Object.clone()
. Ne le fais pas.Object.clone()
présente quelques problèmes majeurs et son utilisation est déconseillée dans la plupart des cas. Veuillez consulter le point 11 de « Effective Java » de Joshua Bloch pour une réponse complète. Je crois que vous pouvez utiliser en toute sécuritéObject.clone()
sur des tableaux de type primitif, mais à part cela, vous devez être judicieux sur l'utilisation correcte et la substitution du clone.Les schémas qui reposent sur la sérialisation (XML ou autre) sont délicats.
Il n'y a pas de réponse facile ici. Si vous souhaitez copier en profondeur un objet, vous devrez parcourir le graphique d'objet et copier chaque objet enfant de manière explicite via le constructeur de copie de l'objet ou une méthode d'usine statique qui à son tour copie en profondeur l'objet enfant. Les objets immuables (par exemple les
String
s) n'ont pas besoin d'être copiés. En passant, vous devriez privilégier l'immuabilité pour cette raison.la source
Vous pouvez faire une copie complète avec sérialisation sans créer de fichiers.
Votre objet que vous souhaitez copier en profondeur devra
implement serializable
. Si la classe n'est pas définitive ou ne peut pas être modifiée, étendez la classe et implémentez sérialisable.Convertissez votre classe en un flux d'octets:
Restaurez votre classe à partir d'un flux d'octets:
la source
instance
dans ce cas?Vous pouvez faire un clone profond basé sur la sérialisation en utilisant
org.apache.commons.lang3.SerializationUtils.clone(T)
en dans Apache Commons Lang, mais attention - les performances sont épouvantables.En général, il est recommandé d'écrire vos propres méthodes de clonage pour chaque classe d'un objet dans le graphe d'objet nécessitant un clonage.
la source
org.apache.commons.lang.SerializationUtils
Une façon d'implémenter la copie approfondie consiste à ajouter des constructeurs de copie à chaque classe associée. Un constructeur de copie prend une instance de «ceci» comme argument unique et en copie toutes les valeurs. Beaucoup de travail, mais assez simple et sûr.
EDIT: notez que vous n'avez pas besoin d'utiliser des méthodes d'accesseur pour lire les champs. Vous pouvez accéder directement à tous les champs car l'instance source est toujours du même type que l'instance avec le constructeur de copie. Évident mais pourrait être négligé.
Exemple:
Modifier: notez que lorsque vous utilisez des constructeurs de copie, vous devez connaître le type d'exécution de l'objet que vous copiez. Avec l'approche ci-dessus, vous ne pouvez pas facilement copier une liste mixte (vous pourrez peut-être le faire avec du code de réflexion).
la source
Toyota
, votre code mettra unCar
dans la liste de destination. Un clonage correct nécessite généralement que la classe fournisse une méthode d'usine virtuelle dont le contrat stipule qu'elle retournera un nouvel objet de sa propre classe; le constructeur de la copie lui-même doitprotected
s'assurer qu'il ne sera utilisé que pour construire des objets dont le type précis correspond à celui de l'objet copié).Vous pouvez utiliser une bibliothèque dotée d'une API simple et effectuer un clonage relativement rapide avec réflexion (devrait être plus rapide que les méthodes de sérialisation).
la source
Apache commons offre un moyen rapide de cloner en profondeur un objet.
la source
XStream est vraiment utile dans de tels cas. Voici un code simple pour faire du clonage
la source
Une approche très facile et simple consiste à utiliser Jackson JSON pour sérialiser un objet Java complexe en JSON et le relire.
http://wiki.fasterxml.com/JacksonInFiveMinutes
la source
Pour les utilisateurs de Spring Framework . Utilisation de la classe
org.springframework.util.SerializationUtils
:la source
Pour les objets compliqués et lorsque les performances ne sont pas importantes, j'utilise une bibliothèque json, comme gson pour sérialiser l'objet en texte json, puis désérialiser le texte pour obtenir un nouvel objet.
gson qui basé sur la réflexion fonctionne dans la plupart des cas, sauf que les
transient
champs ne seront pas copiés et les objets avec référence circulaire avec causeStackOverflowError
.la source
Utilisez XStream ( http://x-stream.github.io/ ). Vous pouvez même contrôler les propriétés que vous pouvez ignorer via des annotations ou en spécifiant explicitement le nom de la propriété à la classe XStream. De plus, vous n'avez pas besoin d'implémenter une interface clonable.
la source
La copie en profondeur ne peut être effectuée qu'avec le consentement de chaque classe. Si vous contrôlez la hiérarchie des classes, vous pouvez implémenter l'interface clonable et implémenter la méthode Clone. Sinon, il est impossible de faire une copie complète en toute sécurité car l'objet peut également partager des ressources non liées aux données (par exemple, les connexions à la base de données). En général, cependant, la copie en profondeur est considérée comme une mauvaise pratique dans l'environnement Java et doit être évitée via les pratiques de conception appropriées.
la source
la source
J'ai utilisé Dozer pour cloner des objets java et c'est génial, la bibliothèque Kryo est une autre excellente alternative.
la source
BeanUtils fait un très bon travail de clonage en profondeur des haricots.
la source
1)
Ici, votre classe MyPerson et MyAddress doit implémenter une interface serilazable
la source
Utiliser Jackson pour sérialiser et désérialiser l'objet. Cette implémentation ne nécessite pas que l'objet implémente la classe Serializable.
la source