Java Koleksiyonlar listesi nasıl kopyalanır


141

Bir tane var ArrayListve tam olarak kopyalamak istiyorum. Mümkün olduğunda, birinin düzeltmek için biraz zaman harcadığı varsayımı üzerine faydalı sınıfları kullanıyorum. Doğal olarak, Collectionsbir kopyalama yöntemi içeren bir sınıfla bitiriyorum.

Şunu varsayalım:

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

Bu başarısız olur çünkü temelde btutacak kadar büyük olmadığını düşünür a. Evet biliyorum bboyutu 0, ama şimdi yeterince büyük olmalı, değil mi? bÖnce doldurmam gerekirse, o zaman Collections.copy()aklımda tamamen işe yaramaz bir işlev haline gelir. Yani, (şimdi yapacağım) bir kopyalama fonksiyonunu programlama dışında bunu yapmanın uygun bir yolu var mı?


Collections.copy () dokümanı, "Hedef listesi en azından kaynak listesi kadar uzun olmalıdır" diyor.
DJClayworth

21
Kabul edilen cevabın doğru olduğunu düşünmüyorum
Bozho

3
Yanlış bir yanıtı kabul ettin, Jasper Floor. Umarım kodunuzda yanlış bilgi kullanmamışsınızdır!
Malcolm

Yanıtlar:


115

çağrı

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

aiçinde sığ bir kopya oluşturur b. Tüm unsurlar, içinde bbulundukları sırayla var olacaktır a(bir düzeni olduğunu varsayarak).

Benzer şekilde,

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

ayrıca aiçinde sığ bir kopya oluşturur b. İlk parametre,, tüm öğelerini içerecek bkadar yeterli kapasiteye (boyut değil) sahip değilse a, bir IndexOutOfBoundsException. Beklenti, çalışmak için hiçbir tahsisatın gerekmeyeceğidir Collections.copyve eğer varsa, bu istisnayı atar. Kopyalanan koleksiyonun önceden konumlandırılmasını gerektiren bir optimizasyon ( b), ancak genellikle garip yan etkileri olmayan, yukarıda gösterilen gibi yapıcı tabanlı alternatifler göz önüne alındığında, gerekli kontroller nedeniyle özelliğin buna değer olduğunu düşünmüyorum.

Derin bir kopya oluşturmak için List, her iki mekanizma yoluyla, altta yatan tür hakkında karmaşık bilgiye sahip olmak gerekir. StringJava'da (ve bu konuda .NET) değiştirilemeyen s durumunda, derin bir kopyaya bile ihtiyacınız yoktur. Durumunda, MySpecialObjectnasıl derin bir kopyasını yapacağınızı bilmeniz gerekir ve bu genel bir işlem değildir.


Not: Başlangıçta kabul edilen cevap Collections.copyGoogle için en iyi sonuçtu ve yorumlarda belirtildiği gibi yanlıştı.


1
@ncasas Evet öyle. Java genel bir "kopya" işlevi olmadığı gerçeğini ağıt yakıyorum. Pratikte, çoğu zaman diğer yazarların sınıfları için klon () uygulamadığını; bir nesneyi herhangi bir kopyasını yapma yeteneği olmadan bırakır. Ya da daha da kötüsü, klon işlevini kullanılamaz hale getiren (güvenilir ve pratik bir "neler olduğunu bilmek" anlamında) ya hiç ya da kötü belgelemelere sahip uygulanmış bir klon yöntemi görüyorum.
Malcolm

133

b3, ancak 0 büyüklüğünde bir kapasiteye sahiptir. Bir çeşit tampon kapasitesine sahip olması bir uygulama detayıdır - arayüzün bir parçası değildir , bu yüzden kullanmaz. Özel durum için çirkin olurdu .ArrayListListCollections.copy(List, List)ArrayList

MrWiggles'in belirttiği gibi, bir koleksiyon alan ArrayList yapıcısını kullanmak, verilen örnekte yoludur.

Daha karmaşık senaryolar için (gerçek kodunuzu da içerebilir), Guava içindeki koleksiyonları yararlı bulabilirsiniz .


58

Sadece yap:

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

ArrayList öğelerin kopyalanması için başka bir Koleksiyon kabul edecek bir kurucuya sahiptir


7
yorumların altındaki biri gibi, bu sığ bir kopyadır. Aksi halde bu iyi bir cevap olurdu. Sanırım bunu belirtmeliydim. Boşver, yine de devam ettim.
Jasper Floor

11
Dizelerin bir listesi için, Stringnesneler değişmez olduğu için derin kopya önemli değildir .
Derek Mahar

17

Stephen Katulka'nın cevabı (kabul edilen cevap) yanlış (ikinci kısım). Bunun Collections.copy(b, a);derin bir kopyasını yaptığını açıklar; Her ikisi de new ArrayList(a);ve Collections.copy(b, a);sadece sığ bir kopyasını yapın. Fark, kurucunun yeni bellek ayırmasıdır ve copy(...)vermez, bu da orada bir performans avantajı olduğu için dizileri yeniden kullanabileceğiniz durumlarda uygun hale getirir.

Java standart API'si derin kopyaların kullanılmasını engellemeye çalışır, çünkü yeni kodlayıcıların bunu düzenli olarak kullanması kötü olur, bu da clone()varsayılan olarak herkese açık olmamasının nedenlerinden biri olabilir .

İçin kaynak kodu Collections.copy(...)552. satırda şu adreste görülebilir: http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Core/Collections-Jar-Zip-Logging-regex/java/util/ Collections.java.htm

Derin bir kopyaya ihtiyacınız varsa, her nesne üzerinde bir for döngüsü ve klon () kullanarak öğeleri manuel olarak tekrarlamanız gerekir.


12

bir listeyi kopyalamanın en basit yolu listeyi yeni listenin yapıcısına iletmektir:

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

b sığ bir kopyası olacak a

Collections.copy(List,List)(Daha önce hiç görmemiştim) kaynağına bakıldığında , elemanlar endeksini indeksle başa çıkmak gibi görünüyor. kullanılarak List.set(int,E)0 irade hedef listesindeki vs vs özellikle itiraf etmesi gerekir javadocs dan temizlemek değil yazma elemanı 0 üzerinde böylece elemanı.

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'

neden 'sığ' kopya diyorsun? - me java noob
Martlark

4
'Sığ kopya' ile, kopyadan sonra b'deki nesnelerin a kopyalarıyla değil aynı nesneler olduğu anlamına gelir.
DJClayworth

1
Collections.copy () için javadoc, "Hedef listesi en azından kaynak listesi kadar uzun olmalıdır" diyor.
DJClayworth

Sanırım sadece işlevin gerçekte ne yaptığını görmek için bana birkaç bakış aldı ve sorucının tam olarak ne ile biraz karıştığını görebiliyorum
Gareth Davis

önemli olduğundan emin değilim? String değişmez olduğu için yalnızca referanslar aynı değildir. ancak, her iki listedeki bir öğeyi değiştirmeye çalışsanız bile, diğer listedeki aynı öğeyi hiçbir zaman değiştirmez
David T.

9
List b = new ArrayList(a.size())

boyutu ayarlamıyor. Başlangıç ​​kapasitesini ayarlar (yeniden boyutlandırılmadan önce kaç öğeye sığabileceği). Bu durumda kopyalamanın daha basit bir yolu:

List b = new ArrayList(a);

8

Hoijui'den bahsedildiği gibi. Stephen Katulka'dan seçilen cevap Collections.copy hakkında yanlış bir yorum içeriyor. Yazar muhtemelen kabul etti, çünkü ilk kod satırı istediği kopyayı yapıyordu. Collections.copy'ye yapılan ek çağrı tekrar kopyalanır. (Sonuçta kopya iki kez olur).

İşte kanıtlamak için kod.

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.
        }
    }
}

5

Buradaki yanıtların çoğu sorunu anlamıyor, kullanıcı ilk listeden ikinci listeye öğelerden bir KOPYA'ya sahip olmak istiyor, hedef liste öğeleri yeni nesneler ve orijinal listenin öğelerine başvurmuyor. (ikinci listenin bir öğesinin değiştirilmesi, kaynak listesinin karşılık gelen öğesinin değerlerini değiştirmemelidir.) Değiştirilebilir nesneler için ArrayList (Koleksiyon) yapıcısını kullanamayız çünkü basitçe orijinal liste öğesine başvurur ve kopyalamaz. Kopyalama sırasında her nesne için bir liste klonlayıcıya sahip olmanız gerekir.


5

Neden sadece addAllyöntemi kullanmıyorsunuz :

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

    List b = b.addAll(listA);

//b will be 1, abc

b öğesinde mevcut öğeleriniz olsa veya bazı öğeleri bu öğeden sonra beklemek istiyorsanız, örneğin:

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

3

Bir ArrayList'i kopyalamak istiyorsanız, şunu kullanarak kopyalayın:

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

List a = new ArrayList(b);

3

Dizeler derin kopyalanabilir

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

çünkü değişmezler. Diğer her Nesne - yinelemeli ve kendiniz bir kopyasını yapmalısınız.


8
Bu hala sığ bir kopyadır, çünkü dizi öğelerinin her biri baynı karşılık gelen Stringnesneyi gösterir a. Ancak, bu önemli değildir, çünkü belirttiğiniz gibi Stringnesneler değişmezdir.
Derek Mahar

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

4
Lütfen cevabınıza biraz açıklama ekleyin
Sampada

1
Bu kod soruyu cevaplayabilse de, sorunun nasıl ve / veya neden çözüldüğü hakkında ek bağlam sağlamak yanıtlayıcının uzun vadeli değerini artıracaktır.
Michael Parker

1

Diğer her Nesne - yinelemeli ve kendiniz bir kopyasını yapmalısınız.

Bunu önlemek için Cloneable uygulayın.

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);               
        }
       */

  }

2
"UserList2.add ((User) u.clone ());" olmamalı ?
KrishPrabakar

1

Aşağıdaki çıktı, copy yapıcısını ve Collections.copy () yöntemini kullanmanın sonuçlarını göstermektedir:

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)

Tam programın kaynağı burada: Java List kopyası . Ancak çıktı, java.util.Collections.copy () 'nin nasıl davrandığını görmek için yeterlidir.


1

Google guava kullanıyorsanız, tek satırlık çözüm

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

Bu, değiştirilebilir bir dizi listesi örneği oluşturur.


1

Java 8 null için güvenli olduğunda, aşağıdaki kodu kullanabilirsiniz.

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

Veya bir koleksiyoncu kullanarak

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

0

Bazı değerleri varolan bir koleksiyona kopyalamak için kullanım durumunu düşünürseniz, kopyalama işe yaramaz. Yani eklemek yerine mevcut öğelerin üzerine yazmak istiyorsunuz.

Bir örnek: 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]

Ancak ben kaynak ve hedef toplama başlangıç ​​dizini için ek parametreler yanı sıra saymak için bir parametre alacak bir kopyalama yöntemi beklenir.

Java BUG 6350752'yi inceleyin


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.