Java'da genel bir listeyi nasıl klonlayabilirim?


155

Bir var ArrayList<String>ben bir kopyasını iade etmek istiyorum söyledi. ArrayListaşağıdaki imzayı içeren bir klon yöntemine sahiptir:

public Object clone()

Bu yöntemi çağırdıktan sonra, döndürülen Nesneyi geri nasıl atabilirim ArrayList<String>?


16
Hayır, bu geçerli bir soru. Java, çalışma zamanı türü silme ve tümüyle "gerçek" jenerikleri desteklemez, bu nedenle bu tür ayrıntılar zor olabilir. Ayrıca, Klonlanabilir arayüz ve Object.clone () yöntem mekanizması da benzer şekilde kafa karıştırıcıdır.
Yasadışı Programcı

Tamam, çoğunlukla bu C # kolay. Bu sorudaki yorumları kaldırmamı istiyorsanız lütfen bize bildirin.
Espo

1
Yorum bırakabilirsiniz. Düzenlemelerimin neyle sorun yaşadığımı açıkladığını düşünüyorum.
Kertenkele Bill

2
Yorumunuz tamam, eğer biraz küçümsüyorsa. Java geliştiricilerinin atlaması gereken birçok çemberin .NET geliştiricilerine aptalca geldiğini hayal ediyorum.
Yasadışı Programcı

1
@Oscar, klonlamak istiyor ve klon komutunu çağırmamak istiyor. Klonun gerçekten klonlamadığı ile aynı olmayabilir. Bence mesele bu. Bu gerçekten zor bir soru.
Rafa

Yanıtlar:


62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();

43
Bu, Dizeler için iyi çalışacaktır (sorunun sorulması gereken şeydir), ancak ArrayList.clone'un sığ bir kopya yapacağını belirtmek gerekir, bu nedenle listede değiştirilebilir nesneler varsa, bunlar klonlanmayacak (ve bir tanesini değiştirmeyeceklerdir) bir listede diğer listede de
değişecektir

49
Eski kod dışında herhangi bir şeyde ham türleri kullanmaktan kaçınmalısınız. Kullanmak daha iyi ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
cdmckay

20
Çok kötü ArrayList'in #clone yöntemi var, ancak List'in kendisi yok. İç çekmek.
rogerdpack

12
Alakalı değil ama onunla çalışırken: sol taraftaki List<String>yerine kullanın ArrayList<String>. Koleksiyonlar çoğu zaman bu şekilde kullanılmalıdır.
Christophe Roussy

3
Bir ArrayList çoğaltmak için bu yöntemi kullanamadı. Clone () yöntemi tanınmadı.
Jack

318

Neden klonlamak istiyorsun? Yeni bir liste oluşturmak genellikle daha mantıklıdır.

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

İş bitmiş.


17
Ne tür bir liste olduğunu bilemeyebilirsiniz. Bir LinkedList, MyOwnCustomList veya ArrayList'in bir alt sınıfı olabilir; bu durumda ArrayList'i yenilerken yanlış tür olur.
Steve Kuo

51
Orijinal listenin hangi uygulamada kullanıldığını umursar mıyım? Muhtemelen yeni listenin hangi uygulamayı kullandığını umursuyorum.
Tom Hawtin - tackline

12
@Steve Kuo: İmza, ArrayList(Collection<? extends E> c)argüman olarak ne tür bir liste kullandığınızın önemli olmadığı anlamına geliyor.
cdmckay

3
Yani derin bir kopya değil, değil mi?

4
@YekhezkelYovel Hayır, olmazdı.
Tom Hawtin - taktik

19

Bunun için kullandığım kod:

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

Umut senin için yararlıdır


3
Ve ham türleri kullanmaktan kaçının .. yani ArrayListkullanım yerineArrayList<YourObject>
milosmlar

2
docs.oracle.com/javase/8/docs/api/java/util/… Liste boyutları farklı olduğu için çalışmıyor.
MLProgrammer-CiM

Neden sadece ArrayListyapıcı aşırı yüklenmesini kullanmıyorsunuz ?
Tom Hawtin - taktik

19

Java 8 ile bir akışla klonlanabilir.

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

...

List<AnObject> clone = myList.stream().collect(toList());

Liste <AnObject> xy = yeni ArrayList <> (oldList);
mirzak

1
Bu derin bir kopya değil, bir listenin öğelerindeki değişiklikler diğerinde görülebilir
Inchara

2
Sorunun neresinde derin bir kopya gerektiğini belirtiyor? Varsayım, aynı nesneleri içeren başka bir koleksiyonun gerekli olduğudur. Derin kopyalama yoluna gitmek istiyorsanız yepyeni bir solucan kutusu açıyorsunuz.
Simon Jenkins

Gerçi gerçekten bir klon değil mi? API dokümanlarından "Döndürülen Listenin türü, değiştirilebilirliği, serileştirilebilirliği veya iş parçacığı güvenliği konusunda hiçbir garanti yoktur". Euw, bunlardan birini istemiyorum.toCollectiondaha iyi bir seçim olabilir.
Tom Hawtin - tackline

16

Object.clone () 'un bazı büyük sorunları olduğu ve kullanımının çoğu durumda kullanılması önerilmez. Eksiksiz bir cevap için lütfen Joshua Bloch'un " Etkili Java " ndan Madde 11'e bakınız . Object.clone () öğesini ilkel tip dizilerde güvenle kullanabileceğinize inanıyorum, ancak bunun dışında klonu düzgün bir şekilde kullanma ve geçersiz kılma konusunda makul olmanız gerekiyor. Muhtemelen semantiğinize göre nesneyi açıkça klonlayan bir kopya oluşturucu veya statik fabrika yöntemi tanımlamaktan daha iyidir.


15

Bunun Koleksiyonlar API'sını kullanarak hile yapması gerektiğini düşünüyorum:

Not : kopyalama yöntemi doğrusal zamanda çalışır.

//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);

13
Neden sadece yeni ArrayList <String> (oldList) kullanmıyorsunuz?
cdmckay

4
Bunun doc'den işe yaramayacağına inanıyorum * Hedef listesi en azından kaynak listesi kadar olmalıdır. Daha uzunsa, hedef listesindeki geri kalan öğeler etkilenmez.
Greg Domjan

@GregDomjan bunun en iyi yol olduğunu kabul etmiyorum, ama bunu yapmanın bir yolu. Sorununuzu çözmek için bu kadar basit: List <String> newList = new ArrayList <> (oldList.size ());
nckbrz

1
Java 8 ile ilgili olup olmadığından emin değilim, ancak boyutu belirlerken bile, yine de bir istisna alıyorum IndexOutOfBoundsException: destination.size () <source.size (): 0 <2 üzerinde KullanıyorList<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); gibi görünüyor copyList.addAll(original);iyi bir alternatif
Gene Bo

@GeneBo Boyutu belirtmiyor. Çok farklı bir şey olan kapasiteyi belirtiyor. Gizem intargümanlarının sevinci . Neden iyi eski yerine bu belirsiz statik yöntemi kullanmak istediğiniz hakkında hiçbir fikrim yok addAll.
Tom Hawtin - tackline

8

AddAll kullanarak iyi çalışır buluyorum.

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

jenerik sözdizimi yerine parantez kullanılır


5
Bu, Dizeler için çalışır, ancak değiştirilebilir nesneler için çalışmaz. Bunları da klonlamak isteyeceksiniz.
jodonnell

Evet, sorusu ipler için. Ve bu durumda döküm malzemelerini gerçekten sevmeyen jenerikler sorunu var.
Allain Lalonde

Ayrıca, ArrayList.clone yalnızca sığ bir klon yapar, bu nedenle listedeki değiştirilebilir nesneler de bu yöntemle klonlanmaz.
08:23

Bu ArrayList <String> btw olmalıdır. Ayrıca, daha az yazdığı ve net olduğu için yeni ArrayList <String> (orijinal) kullanmaktan daha iyidir.
cdmckay

4
Neden sadece kullanmıyorsunuz new ArrayList<String>(original)?
user102008

6

Listeyi bir alıcıda iade edebilmek için bunu yapmak daha iyi olur:

ImmutableList.copyOf(list);

4

java.util.ListSizin gibi genel bir arayüzü klonlamak için sadece onu yayınlamanız gerekir. işte size bir örnek:

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

Biraz zor ama işe yarıyor, eğer geri dönmekle sınırlıysanız List arayüze , böylece listenizden sonra istediği zaman uygulayabilirsiniz.

Bu cevabın son cevaba yakın olduğunu biliyorum, ama cevabım List-genel ebeveyn ile çalışırken tüm bunların nasıl yapılacağını cevaplıyorArrayList


2
her zaman doğru olmayan bir ArrayList olacağını varsayıyorsunuz
jucardi

@JuanCarlosDiaz olacak, bu sorulan soru, ArrayList hakkındaydı, bu yüzden ArrayList için cevap verdim :)
Ahmed Hamdy

2

ArrayLists'i kopyalarken çok dikkatli olun. Java'da klonlama sığdır. Bu, üyelerini değil Arraylistin kendisini klonlayacağı anlamına gelir. Dolayısıyla, bir ArrayList X1'iniz varsa ve bunu X2'ye klonlarsanız, X2'deki herhangi bir değişiklik X1'de de ortaya çıkacaktır ve bunun tersi de geçerlidir. Klonladığınızda yalnızca orijinalindeki aynı öğelere işaret eden yeni bir ArrayList oluşturacaksınız.


2

Bunun da çalışması gerekir:

ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()

2
List<String> shallowClonedList = new ArrayList<>(listOfStrings);

Bunun derin bir kopya değil, yalnızca sığ olduğunu unutmayın. yeni bir liste alırsınız, ancak girişler aynıdır. Bu sadece dizeler için sorun değil. Liste girişleri kendileri nesne olduğunda daha zorlayıcı olun.


1
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();

1

Ben bir java profesyoneli değilim, ama aynı problemim var ve bu yöntemle çözmeye çalıştım. (T'nin bir kopya yapıcısı olduğunu varsayalım).

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

ListUygulama sınıfının ortak no-arg yapıcısı olduğuna dair bir garanti yoktur. Örneğin, Listdöner tarafından s Array.asList, List.ofya List.subListmuhtemelen yoktur.
Tom Hawtin - tackline
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.