Jak skopiować listę kolekcji Java

Mam ArrayList i chcę to dokładnie skopiować. Używam klas użyteczności, gdy jest to możliwe, zakładając, że ktoś poświęcił trochę czasu na poprawienie tego. Więc naturalnie, kończę z klasą Collections, która zawiera metodę kopiowania.

Załóżmy, że mam:

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);

To się nie powiedzie, ponieważ w zasadzie myśli, że {[3] }nie jest wystarczająco duży, aby utrzymać a. Tak, Wiem, że b ma rozmiar 0, ale powinien być wystarczająco duży, prawda? Jeśli najpierw muszę wypełnić b, to Staje się całkowicie bezużyteczną funkcją w moim umyśle. Tak więc, poza programowaniem funkcji kopiowania (co zamierzam zrobić teraz), czy istnieje odpowiedni sposób, aby to zrobić?

Author: moffeltje, 2009-03-27

18 answers

Wywołanie

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

Tworzy płytką kopię a wewnątrz b. Wszystkie elementy będą istnieć w b w dokładnie tej samej kolejności, w jakiej znajdowały się w a (zakładając, że miały kolejność).

Podobnie, wywołanie

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

Tworzy również płytką kopię a wewnątrz b. Jeśli pierwszy parametr b nie ma wystarczającej pojemności (Nie wielkości), aby zawierał wszystkie elementy a, to rzuci IndexOutOfBoundsException. Oczekuje się, że żadne przydziały nie będą wymagane przez Collections.copy do pracy, a jeśli są, to rzuca ten wyjątek. Jest to optymalizacja wymagająca prealokacji skopiowanej kolekcji (b), ale generalnie nie uważam, że ta funkcja jest tego warta ze względu na wymagane kontrole, biorąc pod uwagę alternatywy oparte na konstruktorze, takie jak ta pokazana powyżej, które nie mają dziwnych skutków ubocznych.

Aby utworzyć głęboką kopię, List, za pomocą dowolnego mechanizmu, musiałby mieć skomplikowaną wiedzę o podstawowym typie. W przypadku z String s, które są niezmienne w Javie (i w tym przypadku. NET), nie potrzebujesz nawet głębokiej kopii. W przypadku MySpecialObject, musisz wiedzieć, jak zrobić jego głęboką kopię i nie jest to operacja ogólna.


Uwaga: pierwotnie zaakceptowana odpowiedź była najlepszym wynikiem dla Collections.copy W Google, i była całkowicie błędna, jak wskazano w komentarzach.

 118
Author: Stephen Katulka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-30 16:22:46

b ma pojemność 3, ale Rozmiar 0. Fakt, że ArrayList ma jakąś pojemność bufora jest szczegółem implementacji - nie jest częścią interfejsu List, więc Collections.copy(List, List) go nie używa. Byłoby brzydko, gdyby to był przypadek specjalny ArrayList.

Jak wskazał MrWiggles, użycie konstruktora ArrayList, który pobiera zbiór, jest drogą do tego w podanym przykładzie.

W przypadku bardziej skomplikowanych scenariuszy (które mogą zawierać twój prawdziwy kod), Możesz znaleźć zbiory w przydatne.

 133
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-07 18:56:34

Po prostu zrób:

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

ArrayList ma konstruktor, który zaakceptuje inną kolekcję do skopiowania elementów z

 59
Author: tddmonkey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-27 11:25:11

Odpowiedź Stephena Katulki (odpowiedź zaakceptowana) jest błędna (druga część). Wyjaśnia, że Collections.copy(b, a); robi głęboką kopię, której nie robi. Zarówno new ArrayList(a);, jak i Collections.copy(b, a); wykonują tylko płytką kopię. Różnica polega na tym, że konstruktor przydziela nową pamięć, a copy(...) nie, co czyni go odpowiednim w przypadkach, w których można ponownie użyć macierzy, ponieważ ma tam przewagę wydajnościową.

Standardowe API Javy stara się zniechęcić do używania głębokich kopii, ponieważ byłoby źle, gdyby nowi koderzy używali tego regularnie, co może być również jednym z powodów, dla których clone() nie jest domyślnie publiczna.

Kod źródłowy dla Collections.copy(...) można zobaczyć w linii 552 Na: http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Core/Collections-Jar-Zip-Logging-regex/java/util/Collections.java.htm

Jeśli potrzebujesz głębokiej kopii, musisz ręcznie iterować elementy, używając pętli for I Clone () dla każdego obiektu.

 17
Author: hoijui,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-24 16:38:33

Najprostszym sposobem skopiowania listy jest przekazanie jej do konstruktora nowej Listy:

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

b będzie płytką kopią a

Patrząc na źródło Collections.copy(List,List) (nigdy wcześniej tego nie widziałem) wydaje się, że jest do radzenia sobie z elementami indeks po indeksie. użycie List.set(int,E) w ten sposób element 0 nadpisze element 0 na liście docelowej itd itd. Muszę przyznać, że nie jest to szczególnie jasne od Javadoc.

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
Author: Gareth Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-11 13:58:22
List b = new ArrayList(a.size())

Nie ustawia rozmiaru. Ustawia początkową pojemność (czyli ile elementów może zmieścić się przed zmianą rozmiaru). Prostszym sposobem kopiowania w tym przypadku jest:

List b = new ArrayList(a);
 9
Author: cletus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-27 11:27:56

Jak wspomina hoijui. Wybrana odpowiedź Stephena Katulki zawiera komentarz dotyczący kolekcji.kopia, która jest niepoprawna. Autor prawdopodobnie zaakceptował to, ponieważ pierwsza linijka kodu wykonywała kopię, którą chciał. Dodatkowe wezwanie do zbiórki.Kopiuj tylko Kopiuj jeszcze raz. (W wyniku czego Kopia dzieje się dwa razy).

Oto kod, który to udowodni.

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
Author: Michael Welch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-30 13:29:39

Większość odpowiedzi tutaj nie zdaje sobie sprawy z problemu, użytkownik chce mieć kopię elementów z pierwszej listy do drugiej listy, elementy listy docelowej są nowymi obiektami, a nie odwołaniem do elementów oryginalnej listy. (czyli zmiana elementu drugiej listy nie powinna zmieniać wartości dla odpowiedniego elementu listy źródłowej.) Dla zmiennych obiektów nie możemy użyć konstruktora ArrayList (Collection), ponieważ będzie on w prosty sposób odnosił się do oryginalnego elementu list i nie będzie kopiował. Potrzebujesz aby mieć listę cloner dla każdego obiektu podczas kopiowania.

 5
Author: yasirmcs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-10 09:29:01

Dlaczego nie użyjesz metody addAll:

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

    List b = b.addAll(listA);

//b will be 1, abc

Nawet jeśli masz istniejące elementy w b lub chcesz umieścić po nim niektóre elementy, takie jak:

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
Author: Vin.X,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-01 01:04:31

Jeśli chcesz skopiować Arraylistę, skopiuj ją za pomocą:

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

List a = new ArrayList(b);
 3
Author: Martin C.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-27 11:25:59

Ciągi mogą być głęboko kopiowane za pomocą

List<String> b = new ArrayList<String>(a);
Ponieważ są niezmienne. Każdy inny obiekt nie -- > trzeba iterację i zrobić kopię samemu.
 3
Author: felix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-05 16:42:59
private List<Item> cloneItemList(final List<Item> items)
    {
        Item[] itemArray = new Item[items.size()];
        itemArray = items.toArray(itemArray);
        return Arrays.asList(itemArray);
    }
 3
Author: Raen K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-27 14:48:41

Każdy inny obiekt nie -- > musisz iterować i zrobić kopię samemu.

Aby uniknąć tego implementacji 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
Author: Juan Castillo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-28 06:10:54

Poniższe wyjście ilustruje wyniki użycia konstruktora kopiującego i kolekcji.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)

Źródło pełnego programu znajduje się tutaj: Java List copy . Ale wyjście wystarczy, aby zobaczyć, jak java.util.Kolekcje.copy ()

 1
Author: pwojnowski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-30 17:40:33

A jeśli używasz google guava, rozwiązaniem jednej linii byłoby

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

Tworzy zmienną instancję listy tablic.

 1
Author: vsingh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-28 22:13:46

Ponieważ Java 8 jest bezpieczna dla null, możesz użyć następującego kodu.

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

Lub za pomocą kolektora

List<String> b = Optional.ofNullable(a)
                         .map(List::stream)
                         .orElseGet(Stream::empty)
                         .collect(Collectors.toList())
 1
Author: Nicolas Henneaux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-14 12:34:10

Kopiowanie nie jest bezużyteczne, jeśli wyobrażasz sobie przypadek użycia do skopiowania niektórych wartości do istniejącej kolekcji. Czyli chcesz nadpisać istniejące elementy zamiast wstawiać.

Przykład: a = [1,2,3,4,5] b = [2,2,2,2,3,3,3,3,3,4,4,4,4,] a.copy(b) = [1,2,3,4,5,3,3,3,3,4,4,4]

Spodziewałbym się jednak metody kopiowania, która pobierałaby dodatkowe parametry dla indeksu początkowego kolekcji źródłowej i docelowej, a także parametru dla count.

Zobacz błąd Javy6350752

 0
Author: ordnungswidrig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-27 17:16:14

Aby zrozumieć dlaczego zbiory.copy() rzuca wywołanie IndexOutOfBoundsException chociaż tablica pomocnicza listy docelowej jest wystarczająco duża (poprzez wywołanie size () na sourceList), zobacz odpowiedź Abhay Yadav w tym powiązanym pytaniu: Jak skopiować Javę.util.Lista do innej Javy.util.Lista

 -1
Author: volkerk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-27 18:48:25