152 votes

Comment copier la liste des collections Java

J'ai un ArrayList et je veux le copier exactement. J'utilise des classes d'utilité lorsque c'est possible, en partant du principe que quelqu'un a passé du temps à le rendre correct. Donc naturellement, je me retrouve avec le Collections qui contient une méthode de copie.

Supposons que j'ai les éléments suivants :

List<String> a = new ArrayList<String>();
a.add("a");
a.add("b");
a.add("c");
List<String> b = new ArrayList<String>(a.size());

Collections.copy(b,a);

Cela échoue parce que, fondamentalement, il pense b n'est pas assez grand pour contenir a . Oui, je sais. b a la taille 0, mais il devrait être assez grand maintenant, n'est-ce pas ? Si je dois remplir b d'abord, puis Collections.copy() devient une fonction complètement inutile dans mon esprit. Donc, à part programmer une fonction de copie (ce que je vais faire maintenant), y a-t-il une façon correcte de faire cela ?

1 votes

La doc pour Collections.copy() dit "La liste de destination doit être au moins aussi longue que la liste source".

21 votes

Je ne pense pas que la réponse acceptée soit correcte.

3 votes

Vous avez accepté une réponse incorrecte, étage Jasper. J'espère sincèrement que vous n'avez pas utilisé les mauvaises informations dans votre code !

135voto

Jon Skeet Points 692016

b a un capacité de 3, mais un taille de 0. Le fait que ArrayList a une sorte de capacité de mémoire tampon est un détail d'implémentation - il ne fait pas partie de l'architecture du système. List l'interface, donc Collections.copy(List, List) ne l'utilise pas. Il serait laid de mettre en majuscules spéciales ArrayList .

Comme l'a indiqué tddmonkey, l'utilisation du constructeur ArrayList qui prend une collection est la solution dans l'exemple fourni.

Pour des scénarios plus compliqués (qui peuvent très bien inclure votre code réel), vous pouvez trouver les collections dans la section Goyave utile.

129voto

Stephen Katulka Points 872

Appel à

List<String> b = new ArrayList<String>(a);

crée une copie superficielle de a sur b . Tous les éléments existeront dans b dans l'ordre exact où ils étaient dans a (en supposant qu'il y ait une commande).

De même, appeler

// note: instantiating with a.size() gives `b` enough capacity to hold everything
List<String> b = new ArrayList<String>(a.size());
Collections.copy(b, a);

crée également une copie superficielle de a sur b . Si le premier paramètre, b n'a pas assez de capacité (pas de taille) pour contenir tous les a il lancera un IndexOutOfBoundsException . On s'attend à ce qu'aucune allocation ne soit requise d'ici le 1er janvier 2010. Collections.copy pour fonctionner, et si c'est le cas, l'exception est levée. L'optimisation consiste à exiger que la collection copiée soit pré-allouée ( b ), mais je ne pense généralement pas que cette fonctionnalité en vaille la peine en raison des vérifications nécessaires, étant donné les alternatives basées sur les constructeurs, comme celle présentée ci-dessus, qui n'ont pas d'effets secondaires bizarres.

Pour créer une copie profonde, le List par le biais de l'un ou l'autre mécanisme, devrait avoir une connaissance approfondie du type sous-jacent. Dans le cas de String qui sont immuables en Java (et en .NET d'ailleurs), vous n'avez même pas besoin d'une copie profonde. Dans le cas de MySpecialObject vous devez savoir comment en faire une copie profonde, ce qui n'est pas une opération générique.


Remarque : la réponse initialement acceptée était le premier résultat pour Collections.copy dans Google, et c'était carrément faux comme le soulignent les commentaires.

2 votes

@ncasas Oui, c'est vrai. Je déplore le fait qu'il n'existe pas de fonction générique de "copie" en Java. Dans la pratique, je constate trop souvent que d'autres auteurs n'ont pas implémenté clone() pour leurs classes, ce qui nous prive de la possibilité de faire une quelconque copie d'un objet. Ou, pire encore, je vois une méthode clone implémentée sans documentation ou avec une documentation médiocre, ce qui rend la fonction clone inutilisable (dans un sens fiable et pratique "savoir ce qui se passe").

63voto

MrWiggles Points 6622

Fais-le :

List a = new ArrayList(); 
a.add("a"); 
a.add("b"); 
a.add("c"); 
List b = new ArrayList(a);

ArrayList possède un constructeur qui acceptera une autre Collection pour copier les éléments de celle-ci.

10 votes

Comme le fait remarquer quelqu'un ci-dessous, il s'agit d'une copie superficielle. Sinon, ça aurait été une bonne réponse. Je suppose que j'aurais dû le préciser. Tant pis, je suis passé à autre chose de toute façon.

13 votes

Pour une liste de chaînes de caractères, la copie profonde n'est pas importante puisque String sont immuables.

1 votes

Il ne s'agit pas seulement d'une liste de chaînes de caractères ! Puisqu'il s'agit d'un type brut, il peut ajouter n'importe quel type immuable ( String ) ou mutable( Date ). Et lorsqu'il s'agit de données mutables que vous ajoutez à cette liste brute, il s'agit essentiellement d'une référence que vous copiez dans l'autre liste.

19voto

hoijui Points 960

La réponse de Stephen Katulka (réponse) est faux (la deuxième partie). Il explique qu' Collections.copy(b, a); t une copie en profondeur, ce qui n'est pas. Les deux, new ArrayList(a); et Collections.copy(b, a); seulement faire une copie. La différence, c'est que le constructeur alloue de la mémoire, et copy(...) ne le fait pas, qui le rend approprié dans les cas où vous pouvez réutiliser des tableaux, comme il a un avantage de performance.

La norme Java API essaie de décourager l'utilisation de la profondeur des copies, comme il serait mauvais si de nouveaux pirates utiliser sur une base régulière, qui peut aussi être une des raisons pourquoi clone() n'est pas public par défaut.

Le code source pour Collections.copy(...) peut être vu sur la ligne de 552 à: http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Core/Collections-Jar-Zip-Logging-regex/java/util/Collections.java.htm

Si vous avez besoin d'une copie en profondeur, vous devez effectuer une itération sur les éléments manuellement, à l'aide d'une boucle for et un clone() de chaque objet.

13voto

Gareth Davis Points 16190

La façon la plus simple de copier une liste est de la passer au constructeur de la nouvelle liste :

List<String> b = new ArrayList<>(a);

b sera une copie superficielle de a

En regardant la source de Collections.copy(List,List) (je ne l'avais jamais vu auparavant) il semble que ce soit pour copier les éléments indice par indice. en utilisant List.set(int,E) Ainsi, l'élément 0 écrasera l'élément 0 dans la liste des cibles, etc. etc. Ce n'est pas très clair dans les javadocs, je dois l'admettre.

List<String> a = new ArrayList<>(a);
a.add("foo");
b.add("bar");

List<String> b = new ArrayList<>(a); // shallow copy 'a'

// the following will all hold
assert a.get(0) == b.get(0);
assert a.get(1) == b.get(1);
assert a.equals(b);
assert a != b; // 'a' is not the same object as 'b'

0 votes

Pourquoi dites-vous "copie superficielle" ? - moi java noob

4 votes

Par "copie superficielle", il entend qu'après la copie, les objets de b sont les mêmes que ceux de a, et non des copies de ceux-ci.

1 votes

La javadoc pour Collections.copy() dit "La liste de destination doit être au moins aussi longue que la liste source".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X