web-dev-qa-db-fra.com

Recommandation de l'utilitaire Deep Clone

Existe-t-il un utilitaire de clonage en profondeur pour les collections Java:

  • Tableaux
  • Listes
  • Plans

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 ...

71
Juraj

Je pense que la réponse verte précédente était mauvaise , pourquoi pourriez-vous demander?

  • Il ajoute beaucoup de code
  • Il vous oblige à répertorier tous les champs à copier et à le faire
  • Cela ne fonctionnera pas pour les listes lors de l'utilisation de clone () (Voici ce que clone () pour HashMap dit: renvoie une copie superficielle de cette instance de HashMap: les clés et les valeurs elles-mêmes ne sont pas clonées.) Donc vous finissez par le faire manuellement (cela fait moi pleure)

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

63
Cojones

Toutes les approches pour copier des objets dans Java ont de graves défauts:

Clone

  1. La méthode clone () est protégée, vous ne pouvez donc pas l'appeler directement à moins que la classe en question ne la remplace par une méthode publique.
  2. clone () n'appelle pas le constructeur. Tout constructeur. Il allouera de la mémoire, affectera le champ interne 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:

  1. Je peux décider quel constructeur appeler et comment initialiser quel champ.
  2. L'initialisation se produit dans un ordre déterministe (de la classe racine à la classe d'instance)
  3. Je peux réutiliser des objets existants et les écraser
  4. Type sûr
  5. Singletons restent singletons

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.

19
Aaron Digulla

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:

  1. effectue un clone profond de n'importe quel objet (même ceux que vous n'écrivez pas vous-même)
  2. vous n'avez pas besoin de garder votre méthode clone () à jour chaque fois que vous ajoutez un champ
  3. vous pouvez cloner des objets qui n'ont pas de constructeur public par défaut
  4. fonctionne avec Spring
  5. (optimisation) ne clone pas les objets immuables connus (comme Integer, String, etc.)
  6. facile à utiliser. Exemple:

    cloner.deepClone (anyObject);

16
Brad Cupit

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.

14

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.

10
John Feminella

Une possibilité consiste à utiliser la sérialisation :

Apache Commons fournit SerializationUtils

5
bruno conde

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.

2

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.

0
Marko