web-dev-qa-db-fra.com

Comment copier la liste des collections Java

J'ai un ArrayList et je veux le copier exactement. J'utilise des classes utilitaires lorsque cela est possible, en partant du principe que quelqu'un a passé du temps à le corriger. Alors naturellement, je me retrouve avec la classe 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 car, fondamentalement, il pense que b n'est pas assez grand pour tenir a. Oui, je sais que b a la taille 0, mais il devrait être assez grand maintenant, n'est-ce pas? Si je dois remplir b en premier, alors Collections.copy() devient une fonction complètement inutile dans mon esprit. Donc, à part la programmation d'une fonction de copie (ce que je vais faire maintenant), existe-t-il un moyen approprié de le faire?

128
Jasper Floor

Appel

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

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

De même, en appelant

// 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 dans b. Si le premier paramètre, b, n'a pas assez de capacity (pas de taille) pour contenir tous les éléments de a, il lancera une IndexOutOfBoundsException. On s'attend à ce qu'aucune allocation ne soit requise par Collections.copy pour fonctionner et, le cas échéant, elle lève cette exception. C'est une optimisation d'exiger que la collection copiée soit préallouée (b), mais je ne pense généralement pas que la fonctionnalité en vaille la peine en raison des vérifications requises, compte tenu des alternatives basées sur le constructeur, comme celle présentée ci-dessus, qui n'ont pas d'effets secondaires étranges.

Pour créer une copie en profondeur, la variable List, via l'un ou l'autre des mécanismes, doit posséder une connaissance complexe du type sous-jacent. Dans le cas de Strings, qui sont immuables en Java (et .NET d'ailleurs), vous n'avez même pas besoin d'une copie complète. Dans le cas de MySpecialObject, vous devez savoir comment en faire une copie complète, ce qui n’est pas une opération générique.


Remarque: la réponse initialement acceptée était le résultat le plus élevé pour Collections.copy dans Google. Elle était totalement fausse, comme indiqué dans les commentaires.

109
Stephen Katulka

b a une capacité de 3, mais une taille de 0. Le fait que ArrayList possède une sorte de capacité de mémoire tampon est un détail de la mise en œuvre - il ne fait pas partie de l'interface List, donc Collections.copy(List, List) n'utilise il. Il serait moche pour cela à un cas spécial ArrayList.

Comme MrWiggles l'a indiqué, l'utilisation du constructeur ArrayList qui prend une collection est la voie à suivre dans l'exemple fourni.

Pour des scénarios plus complexes (pouvant inclure votre code réel), vous pouvez trouver la bibliothèque Google Collections Java utile.

130
Jon Skeet

Il suffit de faire:

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

ArrayList a un constructeur qui acceptera une autre Collection pour copier les éléments de

58
tddmonkey

La réponse de Stephen Katulka (réponse acceptée) est fausse (la deuxième partie) . Elle explique que Collections.copy(b, a); effectue une copie en profondeur, ce qui n'est pas le cas. new ArrayList(a); et Collections.copy(b, a); ne font qu'une copie superficielle. La différence est que le constructeur alloue une nouvelle mémoire et que copy(...) ne le fait pas, ce qui le rend approprié dans les cas où vous pouvez réutiliser des tableaux, car cela présente un avantage en termes de performances.

L'API standard Java tente de décourager l'utilisation de copies complètes, car il serait mauvais que de nouveaux codeurs l'utilisent régulièrement, ce qui peut également expliquer pourquoi clone() n'est pas public par défaut.

Le code source de Collections.copy(...) est affiché à la ligne 552 à l'adresse suivante: 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 complète, vous devez effectuer une itération manuelle sur les éléments, en utilisant une boucle for et un clone () sur chaque objet.

16
hoijui

le moyen le plus simple de copier une liste est de la transmettre 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 vue auparavant), il semble que ce soit pour gérer l'index des éléments, index par index. Si vous utilisez List.set(int,E), l'élément 0 écrasera l'élément 0 dans la liste des cibles, etc., etc.

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'
12
Gareth Davis
List b = new ArrayList(a.size())

ne définit pas la taille. Il définit la capacité initiale (c'est-à-dire le nombre d'éléments qu'il peut contenir avant de devoir redimensionner). Une manière plus simple de copier dans ce cas est:

List b = new ArrayList(a);
9
cletus

Comme le mentionne Hoijui. La réponse sélectionnée de Stephen Katulka contient un commentaire incorrect sur Collections.copy. L'auteur a probablement accepté parce que la première ligne de code faisait la copie qu'il voulait. L'appel supplémentaire à Collections.copy copie à nouveau. (La copie a lieu deux fois).

Voici le code pour le prouver.

public static void main(String[] args) {

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

    System.out.println("There should be no output after this line.");

    // Note, b is already a shallow copy of a;
    for (int i = 0; i < a.size(); i++) {
        if (a.get(i) != b.get(i)) {
            System.out.println("Oops, this was a deep copy."); // Note this is never called.
        }
    }

    // Now use Collections.copy and note that b is still just a shallow copy of a
    Collections.copy(b, a);
    for (int i = 0; i < a.size(); i++) {
        if (a.get(i) != b.get(i)) {
            System.out.println("Oops, i was wrong this was a deep copy"); // Note this is never called.
        }
    }

    // Now do a deep copy - requires you to explicitly copy each element
    for (int i = 0; i < a.size(); i++) {
        b.set(i, new String(a.get(i)));
    }

    // Now see that the elements are different in each 
    for (int i = 0; i < a.size(); i++) {
        if (a.get(i) == b.get(i)) {
            System.out.println("oops, i was wrong, a shallow copy was done."); // note this is never called.
        }
    }
}
8
Michael Welch

Pourquoi n'utilisez-vous pas la méthode addAll:

    List a = new ArrayList();
         a.add("1");
         a.add("abc");

    List b = b.addAll(listA);

//b will be 1, abc

même si vous avez des éléments existants dans b ou si vous souhaitez y suspendre certains éléments, tels que:

List a = new ArrayList();
     a.add("1");
     a.add("abc");

List b = new ArrayList();
     b.add("x");
     b.addAll(listA);
     b.add("Y");

//b will be x, 1, abc, Y
5
Vin.X

La plupart des réponses ne réalisent pas le problème ici, l'utilisateur veut une copie des éléments de la première liste à la seconde liste, les éléments de la liste de destination sont de nouveaux objets l’élément de la seconde liste ne doit pas changer les valeurs de l’élément correspondant de la liste source.). avoir besoin d'un cloner de liste pour chaque objet lors de la copie.

5
yasirmcs
private List<Item> cloneItemList(final List<Item> items)
    {
        Item[] itemArray = new Item[items.size()];
        itemArray = items.toArray(itemArray);
        return Arrays.asList(itemArray);
    }
3
Raen K

Si vous voulez copier une ArrayList, copiez-la en utilisant:

List b = new ArrayList();
b.add("aa");
b.add("bb");

List a = new ArrayList(b);
3
Martin C.

Les chaînes peuvent être copiées en profondeur avec

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

parce qu'ils sont immuables. Tous les autres objets non -> vous devez itérer et faire une copie par vous-même.

2
felix

La sortie suivante illustre les résultats de l'utilisation du constructeur de copie et de Collections.copy ():

Copy [1, 2, 3] to [1, 2, 3] using copy constructor.

Copy [1, 2, 3] to (smaller) [4, 5]
Java.lang.IndexOutOfBoundsException: Source does not fit in dest
        at Java.util.Collections.copy(Collections.Java:556)
        at com.farenda.Java.CollectionsCopy.copySourceToSmallerDest(CollectionsCopy.Java:36)
        at com.farenda.Java.CollectionsCopy.main(CollectionsCopy.Java:14)

Copy [1, 2] to (same size) [3, 4]
source: [1, 2]
destination: [1, 2]

Copy [1, 2] to (bigger) [3, 4, 5]
source: [1, 2]
destination: [1, 2, 5]

Copy [1, 2] to (unmodifiable) [4, 5]
Java.lang.UnsupportedOperationException
        at Java.util.Collections$UnmodifiableList.set(Collections.Java:1311)
        at Java.util.Collections.copy(Collections.Java:561)
        at com.farenda.Java.CollectionsCopy.copyToUnmodifiableDest(CollectionsCopy.Java:68)
        at com.farenda.Java.CollectionsCopy.main(CollectionsCopy.Java:20)

La source du programme complet est ici: Java List copy . Mais la sortie est suffisante pour voir comment se comporte Java.util.Collections.copy ().

1
pwojnowski

Tous les autres objets non -> vous devez itérer et faire une copie par vous-même.

Pour éviter cela, implémentez Cloneable.

public class User implements Serializable, Cloneable {

    private static final long serialVersionUID = 1L;

    private String user;
    private String password;
    ...

    @Override
    public Object clone() {
        Object o = null;
        try {
          o = super.clone();
        } catch(CloneNotSupportedException e) {
        }
        return o;
     }
 }

....

  public static void main(String[] args) {

      List<User> userList1 = new ArrayList<User>();

      User user1 = new User();
      user1.setUser("User1");
      user1.setPassword("pass1");
      ...

      User user2 = new User();
      user2.setUser("User2");
      user2.setPassword("pass2");
      ...

      userList1 .add(user1);
      userList1 .add(user2);

      List<User> userList2 = new ArrayList<User>();


      for(User u: userList1){
          u.add((User)u.clone());
      }

      //With this you can avoid 
      /*
        for(User u: userList1){
            User tmp = new User();
            tmp.setUser(u.getUser);
            tmp.setPassword(u.getPassword);
            ...
            u.add(tmp);               
        }
       */

  }
1
Juan Castillo

Et si vous utilisez Google Guava, la solution en une ligne serait 

List<String> b = Lists.newArrayList(a);

Cela crée une instance de liste de tableaux mutable.

0
vsingh

Java 8 étant null-safe, vous pouvez utiliser le code suivant.

List<String> b = Optional.ofNullable(a)
                         .map(list -> (List<String>) new ArrayList<>(list))
                         .orElseGet(Collections::emptyList);

Ou en utilisant un collectionneur

List<String> b = Optional.ofNullable(a)
                         .map(List::stream)
                         .orElseGet(Stream::empty)
                         .collect(Collectors.toList())
0
Nicolas Henneaux

Copier n'est pas inutile si vous imaginez le cas d'utilisation permettant de copier certaines valeurs dans une collection existante. C'est à dire. vous voulez écraser les éléments existants au lieu de les insérer.

Un exemple: a = [1,2,3,4,5] b = [2,2,2,2,3,3,3,3,3,4,4,4,] a.copy (b) = [1,2,3,4,5,3,3,3,3,4,4,4]

Cependant, je m'attendrais à une méthode de copie prenant des paramètres supplémentaires pour l'index de démarrage des collections source et cible, ainsi qu'un paramètre pour le nombre.

Voir Java BUG 6350752

0
ordnungswidrig