web-dev-qa-db-fra.com

Comment cloner ArrayList et aussi cloner son contenu?

Comment puis-je cloner une ArrayList et aussi cloner ses éléments en Java?

Par exemple j'ai:

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....

Et je m'attendrais à ce que les objets dans clonedList ne soient pas les mêmes que dans la liste de chiens.

239
palig

Vous devrez itérer sur les éléments et les cloner un par un, en plaçant les clones dans votre tableau de résultats au fur et à mesure.

public static List<Dog> cloneList(List<Dog> list) {
    List<Dog> clone = new ArrayList<Dog>(list.size());
    for (Dog item : list) clone.add(item.clone());
    return clone;
}

Pour que cela fonctionne, vous devrez évidemment que votre classe Dog implémente l'interface Cloneable et substitue la méthode clone().

181
Varkhan

Personnellement, j'ajouterais un constructeur à Dog:

class Dog
{
    public Dog()
    { ... } // Regular constructor

    public Dog(Dog dog) {
        // Copy all the fields of Dog.
    }
}

Ensuite, il suffit de parcourir (comme indiqué dans la réponse de Varkhan):

public static List<Dog> cloneList(List<Dog> dogList) {
    List<Dog> clonedList = new ArrayList<Dog>(dogList.size());
    for (Dog dog : dogList) {
        clonedList.add(new Dog(dog));
    }
    return clonedList;
}

Je trouve l’avantage de cela, c’est que vous n’avez pas besoin de vous tromper avec le matériel cassé Cloneable en Java. Cela correspond également à la façon dont vous copiez les collections Java.

Une autre option pourrait être d’écrire votre propre interface et de l’utiliser. De cette façon, vous pourriez écrire une méthode générique pour le clonage.

180
cdmckay

Toutes les collections standard ont des constructeurs de copie. Utilise les.

List<Double> original = // some list
List<Double> copy = new ArrayList<Double>(original); //This does a shallow copy

clone() a été conçu avec plusieurs erreurs (voir cette question ), il est donc préférable de l'éviter.

De Effective Java 2nd Edition , Point 11: Remplacer le clone judicieusement

Compte tenu de tous les problèmes associés à Clonable, il est prudent de dire que d'autres interfaces ne devraient pas l'étendre, et que les classes conçu pour l'héritage (élément 17) ne devrait pas l'implémenter. En raison de ses nombreux défauts, certains programmeurs experts choisissent tout simplement de ne jamais écrasez la méthode clone et ne l'invoquez jamais sauf peut-être pour copier des tableaux. Si vous concevez une classe pour l'héritage, sachez que si vous choisissez de ne pas fournir une méthode de clonage protégé bien comportée, elle sera impossible pour les sous-classes de mettre en œuvre Cloneable.

Ce livre décrit également les nombreux avantages des constructeurs de copie sur Cloneable/clone.

  • Ils ne reposent pas sur un mécanisme de création d’objet extralinguistique sujet aux risques
  • Ils n'exigent pas l'adhésion inapplicable à des conventions peu documentées
  • Ils n'entrent pas en conflit avec l'utilisation correcte des champs finaux
  • Ils ne jettent pas d'exceptions vérifiées inutiles
  • Ils ne nécessitent pas d'incantation.

Considérez un autre avantage de l’utilisation des constructeurs de copie: supposons que vous ayez un HashSet s et que vous souhaitiez le copier sous la forme TreeSet. La méthode clone ne peut pas offrir cette fonctionnalité, mais c’est facile avec un constructeur de conversion: new TreeSet(s).

128
Rose Perrone

Java 8 fournit un nouveau moyen d’appeler le constructeur de copie ou la méthode de clonage sur l’élément de manière élégante et compacte: Streams, lambdas et collectors .

Constructeur de copie:

List<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toList());

L'expression Dog::new s'appelle un référence de la méthode . Il crée un objet fonction qui appelle un constructeur sur Dog qui prend un autre chien en argument.

Méthode de clonage [1]:

List<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toList());

Obtenir une ArrayList comme résultat

Ou, si vous devez récupérer une ArrayList (au cas où vous voudriez la modifier plus tard):

ArrayList<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toCollection(ArrayList::new));

Mettre à jour la liste en place

Si vous n'avez pas besoin de conserver le contenu d'origine de la liste dogs, vous pouvez utiliser la méthode replaceAll et mettre à jour la liste à la place:

dogs.replaceAll(Dog::new);

Tous les exemples supposent import static Java.util.stream.Collectors.*;.


Collecteur pour ArrayLists

Le collecteur du dernier exemple peut être transformé en une méthode util. Puisque c'est une chose si courante à faire, j'aime personnellement que ce soit court et joli. Comme ça:

ArrayList<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toArrayList());

public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
    return Collectors.toCollection(ArrayList::new);
}

[1] Note sur CloneNotSupportedException:

Pour que cette solution fonctionne, la méthode clone de Dogne doit pas déclare qu'elle jette CloneNotSupportedException. La raison en est que l'argument de map n'est pas autorisé à lancer des exceptions vérifiées.

Comme ça:

    // Note: Method is public and returns Dog, not Object
    @Override
    public Dog clone() /* Note: No throws clause here */ { ...

Cela ne devrait cependant pas être un gros problème, puisque de toute façon, c'est la meilleure pratique. (Effectice Java par exemple donne ce conseil.)

_ {Merci à Gustavo de l'avoir noté.} _


PS:

Si vous le trouvez plus joli, vous pouvez utiliser la syntaxe de référence de la méthode pour faire exactement la même chose:

List<Dog> clonedDogs = dogs.stream().map(Dog::clone).collect(toList());
35
Lii

Fondamentalement, il y a trois façons sans itérer manuellement, 

1 Utilisation du constructeur

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>(dogs);

2 Utilisation de addAll(Collection<? extends E> c)

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(dogs);

3 Utilisation de la méthode addAll(int index, Collection<? extends E> c) avec le paramètre int

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(0, dogs);

NB: Le comportement de ces opérations sera indéfini si la collection spécifiée est modifiée en cours d'opération.

28
javatar

Je pense que la réponse verte actuelle est mauvaise, pourquoi pourriez-vous demander?

  • Cela peut nécessiter d'ajouter beaucoup de code
  • Vous devez lister toutes les listes à copier et le faire

La façon dont la sérialisation est également mauvaise, vous devrez peut-être ajouter Serializable dans tous les sens.

Donc, quelle est la solution:

Bibliothèque Deep-Cloning Java La bibliothèque de clonage est une petite bibliothèque Java open source (licence Apache) qui permet de cloner des objets en profondeur. Les objets ne doivent pas implémenter l'interface clonable. Effectivement, cette bibliothèque peut cloner TOUS les objets Java. Il peut être utilisé par exemple dans les implémentations de cache si vous ne voulez pas que l’objet mis en cache soit modifié ou chaque fois que vous voulez créer une copie complète d’objets. 

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

Découvrez-le sur https://github.com/kostaskougios/cloning

18
Cojones

Vous devrez cloner la variable ArrayList à la main (en la parcourant et en copiant chaque élément dans une nouvelle variable ArrayList), car clone() ne le fera pas pour vous. La raison en est que les objets contenus dans ArrayList peuvent ne pas implémenter Clonable eux-mêmes.

Edit : ... et c'est exactement ce que le code de Varkhan fait.

2
Stephan202

J'ai trouvé un moyen, vous pouvez utiliser JSON pour sérialiser/désérialiser la liste. La liste sérialisée ne contient aucune référence à l'objet d'origine lorsqu'elle n'est pas sérialisée.

Utilisation de gson:

List<CategoryModel> originalList = new ArrayList<>(); // add some items later
String listAsJson = gson.toJson(originalList);
List<CategoryModel> newList = new Gson().fromJson(listAsJson, new TypeToken<List<CategoryModel>>() {}.getType());

Vous pouvez le faire en utilisant jackson et n’importe quelle autre bibliothèque json.

2
sagits

Une mauvaise façon est de le faire avec réflexion. Quelque chose comme ça a fonctionné pour moi.

public static <T extends Cloneable> List<T> deepCloneList(List<T> original) {
    if (original == null || original.size() < 1) {
        return new ArrayList<>();
    }

    try {
        int originalSize = original.size();
        Method cloneMethod = original.get(0).getClass().getDeclaredMethod("clone");
        List<T> clonedList = new ArrayList<>();

        // noinspection ForLoopReplaceableByForEach
        for (int i = 0; i < originalSize; i++) {
            // noinspection unchecked
            clonedList.add((T) cloneMethod.invoke(original.get(i)));
        }
        return clonedList;
    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
        System.err.println("Couldn't clone list due to " + e.getMessage());
        return new ArrayList<>();
    }
}
1
milosmns

Le paquet import org.Apache.commons.lang.SerializationUtils;

Il y a une méthode SerializationUtils.clone(Object);

Exemple

this.myObjectCloned = SerializationUtils.clone(this.object);
0
pacheco

Les autres affiches sont correctes: vous devez itérer la liste et la copier dans une nouvelle liste.

Cependant, si les objets de la liste sont immuables, vous n'avez pas besoin de les cloner. Si votre objet a un graphe d'objet complexe, il devra également être immuable. 

L'autre avantage de l'immuabilité est qu'ils sont également threadsafe.

0
Fortyrunner

Un moyen facile en utilisant commons-lang-2.3.jar cette bibliothèque de Java pour cloner la liste

link télécharger commons-lang-2.3.jar

Comment utiliser

oldList.........
List<YourObject> newList = new ArrayList<YourObject>();
foreach(YourObject obj : oldList){
   newList.add((YourObject)SerializationUtils.clone(obj));
}

J'espère que celui-ci peut être utile.

:RÉ

0
sonida

Je viens de développer une bibliothèque capable de cloner un objet entité et un objet Java.util.List. Téléchargez simplement le fichier jar dans https://drive.google.com/open?id=0B69Sui5ah93EUTloSktFUkctN0U et utilisez la méthode statique cloneListObject (liste). Cette méthode non seulement clone la liste mais également tous les éléments d'entité.

0
Eduardo de Melo

Voici une solution utilisant un type de modèle générique:

public static <T> List<T> copyList(List<T> source) {
    List<T> dest = new ArrayList<T>();
    for (T item : source) { dest.add(item); }
    return dest;
}
0
Andrew Coyte

J'ai toujours utilisé cette option: 

ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone);
0
besartm

pour vous, les objets surchargent la méthode clone ()

class You_class {

    int a;

    @Override
    public You_class clone() {
        You_class you_class = new You_class();
        you_class.a = this.a;
        return you_class;
    }
}

et appelez .clone () pour Vector obj ou ArraiList obj ....

0
RN3KK Nick