Comment cloner une liste générique en Java?

155

J'en ai un ArrayList<String>dont je voudrais renvoyer une copie. ArrayLista une méthode de clonage qui a la signature suivante:

public Object clone()

Après avoir appelé cette méthode, comment convertir l'objet renvoyé en retour ArrayList<String>?

Bill le lézard
la source
16
Non, c'est une question valable. Java ne prend pas en charge les "vrais" génériques, avec l'effacement du type à l'exécution et tout, donc ces types de détails peuvent être délicats. De plus, l'interface clonable et le mécanisme de la méthode Object.clone () sont également déroutants.
Programmeur hors
OK, je fais principalement du C # où c'est vraiment facile. Veuillez me faire savoir si vous souhaitez que je supprime les commentaires de cette question.
Espo
1
Vous pouvez laisser le commentaire. Je pense que mes modifications ont expliqué ce avec quoi j'avais des problèmes.
Bill the Lizard
2
Votre commentaire est OK, même s'il est un peu condescendant. J'imagine que de nombreux obstacles que les développeurs Java doivent franchir semblent ridicules pour les développeurs .NET.
Programmeur hors
1
@Oscar, il veut cloner et ne pas invoquer la commande clone. Ce n'est peut-être pas la même chose si le clone ne clone pas vraiment. Je pense que c'est le point. C'est en effet une question délicate.
Rafa

Réponses:

62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();
Vinko Vrsalovic
la source
43
Cela fonctionnera bien pour les chaînes (ce que la question a posée), mais il convient de noter que ArrayList.clone effectuera une copie superficielle, donc s'il y avait des objets mutables dans la liste, ils ne seront pas clonés (et en changer un dans une liste changera celle-là également dans l'autre liste.
pkaeding
49
Vous devez éviter d'utiliser des types bruts dans autre chose que du code hérité. Vous feriez mieux d'utiliser ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
cdmckay
20
C'est dommage qu'ArrayList ait une méthode #clone, mais List lui-même n'en a pas. Soupir.
rogerdpack
12
Pas de relation mais pendant que vous y êtes: utilisez List<String>plutôt que ArrayList<String>sur le côté gauche. C'est ainsi que les collections doivent être utilisées la plupart du temps.
Christophe Roussy
3
Je ne pouvais pas utiliser cette méthode pour dupliquer une ArrayList. La méthode clone () n'a pas été reconnue.
Jack
318

Pourquoi voudriez-vous cloner? Créer une nouvelle liste est généralement plus logique.

List<String> strs;
...
List<String> newStrs = new ArrayList<>(strs);

Travail accompli.

Tom Hawtin - Tacle
la source
17
Vous ne savez peut-être pas de quel type de liste il s'agit. Il peut s'agir d'une LinkedList, MyOwnCustomList ou une sous-classe de ArrayList, auquel cas la création d'une ArrayList serait du type incorrect.
Steve Kuo
51
Est-ce que je me soucierais de la mise en œuvre utilisée par la liste d'origine? Je me soucie probablement de l'implémentation utilisée par la nouvelle liste.
Tom Hawtin - tackline
12
@Steve Kuo: La signature ArrayList(Collection<? extends E> c)signifie que le type de liste que vous utilisez comme argument n'a pas d'importance.
cdmckay
3
Je veux dire que ce n'est pas une copie complète, n'est-ce pas?
4
@YekhezkelYovel Non, ce ne serait pas le cas.
Tom Hawtin - tackline
19

Voici le code que j'utilise pour cela:

ArrayList copy = new ArrayList (original.size());
Collections.copy(copy, original);

L'espoir est utile pour vous

Allemand
la source
3
Et évitez d'utiliser des types bruts ... donc au lieu d' ArrayListutiliserArrayList<YourObject>
milosmns
2
docs.oracle.com/javase/8/docs/api/java/util/… Ne fonctionne pas car les tailles de liste sont différentes.
MLProgrammer-CiM
Pourquoi n'utiliseriez-vous pas simplement la surcharge de copie du ArrayListconstructeur?
Tom Hawtin - tackline
19

Avec Java 8, il peut être cloné avec un flux.

import static java.util.stream.Collectors.toList;

...

List<AnObject> clone = myList.stream().collect(toList());
Simon Jenkins
la source
Peut être fait comme List <AnObject> xy = new ArrayList <> (oldList);
mirzak
1
Ce n'est pas une copie complète, les changements sur les éléments d'une liste peuvent être vus dans une autre
Inchara
2
Où dans la question spécifie-t-il qu'une copie complète est requise? L'hypothèse est qu'une autre collection contenant les mêmes objets est requise. Si vous voulez emprunter la voie de la copie profonde, vous ouvrez une toute nouvelle boîte de vers.
Simon Jenkins
Pas vraiment un clone, n'est-ce pas? Extrait de la documentation de l'API "Il n'y a aucune garantie sur le type, la mutabilité, la sérialisabilité ou la sécurité des threads de la liste renvoyée". Euw, je n'en veux pas. toCollectionpeut être un meilleur choix.
Tom Hawtin - tackline
16

Sachez que Object.clone () a des problèmes majeurs et que son utilisation est déconseillée dans la plupart des cas. Veuillez consulter le point 11, tiré de « Effective Java » de Joshua Bloch pour une réponse complète. Je pense que vous pouvez utiliser Object.clone () en toute sécurité sur des tableaux de type primitif, mais en dehors de cela, vous devez être judicieux pour utiliser et remplacer correctement le clone. Il vaut probablement mieux définir un constructeur de copie ou une méthode de fabrique statique qui clone explicitement l'objet en fonction de votre sémantique.

Julien Chastang
la source
15

Je pense que cela devrait faire l'affaire en utilisant l'API Collections:

Remarque : la méthode de copie s'exécute en temps linéaire.

//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);
Aaron
la source
13
Pourquoi ne pas simplement utiliser la nouvelle ArrayList <String> (oldList)?
cdmckay
4
Je crois que cela ne fonctionnerait pas, de doc * La liste de destination doit être au moins aussi longue que la liste source. S'il est plus long, les éléments restants de la liste de destination ne sont pas affectés.
Greg Domjan
@GregDomjan non pas que je convienne que c'est la meilleure façon, mais c'est une façon de le faire. Pour contourner votre problème, c'est aussi simple que ceci: List <String> newList = new ArrayList <> (oldList.size ());
nckbrz
1
Je ne sais pas si cela est lié à Java 8, mais même lorsque vous spécifiez la taille, vous obtenez toujours une exception IndexOutOfBoundsException: destination.size () <source.size (): 0 <2 on List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); Il semble que l'utilisation copyList.addAll(original);soit une bonne alternative
Gene Bo
@GeneBo Il ne spécifie pas la taille. Il spécifie la capacité, ce qui est une chose très différente. La joie des intarguments mystérieux . Je ne sais pas pourquoi vous voulez utiliser cette méthode statique obscure au lieu du bon vieux temps addAll.
Tom Hawtin - tackline
8

Je trouve que l'utilisation d'addAll fonctionne bien.

ArrayList<String> copy = new ArrayList<String>();
copy.addAll(original);

les parenthèses sont utilisées plutôt que la syntaxe générique

Allain Lalonde
la source
5
Cela fonctionnera pour les chaînes, mais pas pour les objets mutables. Vous voudrez également les cloner.
jodonnell
Ouais, sa question est pour les cordes. Et il a le problème des génériques qui n'aiment pas vraiment le casting dans ce cas.
Allain Lalonde
De plus, ArrayList.clone ne fera qu'un clone superficiel, donc les objets mutables de la liste ne seront pas non plus clonés à l'aide de cette méthode.
pkaeding le
Cela devrait être ArrayList <String> btw. De plus, il est probablement préférable d'utiliser le nouveau ArrayList <String> (original) car il est moins écrit et tout aussi clair.
cdmckay
4
Pourquoi ne pas simplement utiliser new ArrayList<String>(original)?
user102008
6

Si vous le souhaitez pour pouvoir renvoyer la liste dans un getter, il vaut mieux faire:

ImmutableList.copyOf(list);
Uri Shalit
la source
4

Pour cloner une interface générique comme il java.util.Listvous suffit de la lancer. voici un exemple:

List list = new ArrayList();
List list2 = ((List) ( (ArrayList) list).clone());

C'est un peu délicat, mais cela fonctionne, si vous êtes limité à renvoyer un List interface, donc n'importe qui après vous peut implémenter votre liste quand il le souhaite.

Je sais que cette réponse est proche de la réponse finale, mais ma réponse explique comment faire tout cela pendant que vous travaillez avec List-le parent générique- nonArrayList

Ahmed Hamdy
la source
2
vous supposez que ce sera toujours une ArrayList, ce qui n'est pas vrai
jucardi
@JuanCarlosDiaz va, c'est la question posée, c'était à propos d'ArrayList, alors j'y ai répondu pour ArrayList :)
Ahmed Hamdy
2

Soyez très prudent lorsque vous clonez des ArrayLists. Le clonage en java est superficiel. Cela signifie qu'il ne clonera que l'Arraylist lui-même et non ses membres. Donc, si vous avez une ArrayList X1 et que vous la clonez dans X2, tout changement dans X2 se manifestera également dans X1 et vice-versa. Lorsque vous clonez, vous ne générez qu'une nouvelle ArrayList avec des pointeurs vers les mêmes éléments dans l'original.

Juan Besa
la source
2

Cela devrait également fonctionner:

ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()
pkaeding
la source
2
List<String> shallowClonedList = new ArrayList<>(listOfStrings);

Gardez à l'esprit que ce n'est qu'une copie superficielle et non profonde, c'est-à-dire. vous obtenez une nouvelle liste, mais les entrées sont les mêmes. Ce n'est pas un problème pour les chaînes simples. Get est plus délicat lorsque les entrées de la liste sont elles-mêmes des objets.

Robert
la source
1
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();
Jodonnell
la source
1

Je ne suis pas un professionnel java, mais j'ai le même problème et j'ai essayé de le résoudre par cette méthode. (Cela suppose que T a un constructeur de copie).

 public static <T extends Object> List<T> clone(List<T> list) {
      try {
           List<T> c = list.getClass().newInstance();
           for(T t: list) {
             T copy = (T) t.getClass().getDeclaredConstructor(t.getclass()).newInstance(t);
             c.add(copy);
           }
           return c;
      } catch(Exception e) {
           throw new RuntimeException("List cloning unsupported",e);
      }
}
Petr
la source
Il n'y a aucune garantie que la Listclasse d'implémentation a un constructeur public sans argument. Par exemple, le Lists renvoie par Array.asList, List.ofou List.subListprobablement pas.
Tom Hawtin - tackline