Existe-t-il un utilitaire de clonage en profondeur pour les collections Java:
REMARQUE: préférez une solution sans utiliser la sérialisation, mais avec l'utilisation de la méthode Object.clone (). Je peux être sûr que mon objet personnalisé implémentera la méthode clone () et n'utilisera que des classes standard Java qui sont clonables ...
Je pense que la réponse verte précédente était mauvaise , pourquoi pourriez-vous demander?
Oh et d'ailleurs la sérialisation est également mauvaise, vous devrez peut-être ajouter Serializable partout (cela me fait aussi pleurer).
Alors quelle est la solution:
Java Deep-Cloning library La bibliothèque de clonage est une petite source ouverte (licence Apache) Java bibliothèque qui clone en profondeur les objets. Les objets n'ont pas pour implémenter l'interface Cloneable. En effet, cette bibliothèque peut cloner N'IMPORTE QUEL Java. Elle peut être utilisée c'est-à-dire dans les implémentations de cache si vous ne voulez pas que l'objet mis en cache soit modifié ou quand vous le souhaitez) créer une copie complète des objets.
Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);
Vérifiez-le sur https://github.com/kostaskougios/cloning
Toutes les approches pour copier des objets dans Java ont de graves défauts:
Clone
class
(que vous pouvez lire via getClass()
) et copiera les champs de l'original.Pour plus de problèmes avec clone (), voir le point 11 du livre de Joshua Bloch " Effective Java, Second Edition "
Sérialiser
La sérialisation est encore pire; il a de nombreux défauts de clone()
et puis certains. Joshua a un chapitre entier avec quatre articles pour ce seul sujet.
Ma solution
Ma solution est d'ajouter une nouvelle interface à mes projets:
public interface Copyable<T> {
T copy ();
T createForCopy ();
void copyTo (T dest);
}
Le code ressemble à ceci:
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...
}
}
Malheureusement, je dois copier ce code sur tous mes objets mais c'est toujours le même code, donc je peux utiliser un modèle d'éditeur Eclipse. Avantages:
Pour les types standard Java (comme les collections, etc.), j'utilise une classe utilitaire qui peut les copier. Les méthodes ont des indicateurs et des rappels, donc je peux contrôler la profondeur d'une copie.
Le clonage superficiel d'une collection est facile, mais si vous voulez cloner en profondeur, une bibliothèque vous fera probablement mieux que de la coder manuellement (puisque vous voulez cloner les éléments à l'intérieur la collection aussi).
Tout comme cette réponse , j'ai utilisé la bibliothèque Cloner et spécifiquement les performances l'ont testée contre XStream (qui peut `` cloner '' en sérialisant puis désérialisant) et la sérialisation binaire. Bien que XStream soit très rapide à sérialiser vers/depuis xml, Cloner est beaucoup plus rapide à cloner:
0,0851 ms: xstream (clone par sérialisation/désérialisation)
0,0223 ms: sérialisation binaire (clone par sérialisation/désérialisation)
0,0017 ms: cloneur
* temps moyen pour cloner un objet simple (deux champs) et pas de constructeur public par défaut. Exécutez 10 000 fois.
En plus d'être rapide, voici d'autres raisons de choisir le cloneur:
facile à utiliser. Exemple:
cloner.deepClone (anyObject);
Je suis le créateur du cloner lib, celui que Brad a présenté. Il s'agit d'une solution pour cloner des objets sans avoir à écrire de code supplémentaire (pas besoin d'objets sérialisables ni de méthode impl clone ())
C'est assez rapide comme l'a dit Brad, et récemment j'ai téléchargé une version encore plus rapide. Notez que l'implémentation manuelle d'une méthode clone () sera plus rapide que clone lib, mais là encore, vous devrez écrire beaucoup de code.
Cloner lib a très bien fonctionné pour moi depuis que je l'utilise dans une implémentation de cache pour un site à très fort trafic (~ 1 million de requêtes/jour). Le cache doit cloner environ 10 objets par demande. Il est assez fiable et stable. Mais sachez que le clonage n'est pas sans risque. La bibliothèque peut être configurée pour imprimer toutes les instances de classe qu'elle clone pendant le développement. De cette façon, vous pouvez vérifier s'il clone ce que vous pensez qu'il devrait cloner - les graphiques d'objets peuvent être très profonds et peuvent contenir des références à une quantité étonnamment grande d'objets. Avec clone lib, vous pouvez lui demander de ne pas cloner les objets dont vous ne voulez pas, c'est-à-dire les singletons.
Une façon générale de cloner en profondeur une collection arbitraire est de la sérialiser en un flux, puis de la relire dans une nouvelle collection. Vous allez réhydrater des objets complètement nouveaux qui n'en ont pas relation avec les anciens, autre que d'être des copies identiques.
Consultez la réponse de Bruno pour un lien vers les classes utilitaires de sérialisation Apache Commons , ce qui sera très utile si c'est la route vous décidez de prendre.
Une possibilité consiste à utiliser la sérialisation :
Apache Commons fournit SerializationUtils
J'ai utilisé cette bibliothèque clonage et je l'ai trouvée très utile. Puisqu'il avait quelques limitations (j'avais besoin d'un contrôle plus fin sur le processus de clonage: quel champ, dans quel contexte et à quelle profondeur doit être cloné, etc.), j'ai créé une version étendue de celui-ci. Vous contrôlez le clonage des champs en les annotant dans la classe d'entité.
Pour en avoir un aperçu, voici un exemple de classe:
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
}
}
Plus de détails ici: https://github.com/mnorbi/fluidity-cloning
Il existe également une extension spécifique hibernate au cas où on en aurait besoin.
Utilisez la sérialisation puis la désérialisation, mais sachez que cette approche ne fonctionne qu'avec des classes sérialisables sans champs transitoires. De plus, vos singletons ne seront plus des singletons.