Bagaimana cara mengkloning Daftar generik di Jawa?

155

Saya punya ArrayList<String>yang saya ingin mengembalikan salinan. ArrayListmemiliki metode klon yang memiliki tanda tangan berikut:

public Object clone()

Setelah saya memanggil metode ini, bagaimana cara mengembalikan Objek yang dikembalikan ArrayList<String>?

Bill the Lizard
sumber
16
Tidak, ini pertanyaan yang valid. Java tidak mendukung generik "benar", dengan penghapusan tipe runtime dan sebagainya, sehingga detail seperti ini bisa rumit. Selain itu, antarmuka Cloneable dan mekanisme metode Object.clone () juga membingungkan.
Outlaw Programmer
OK, saya kebanyakan melakukan C # di mana ini sangat mudah. Harap beri tahu saya jika Anda ingin saya menghapus komentar dari pertanyaan ini.
Espo
1
Anda dapat meninggalkan komentar. Saya pikir suntingan saya menjelaskan masalah saya.
Bill the Lizard
2
Komentar Anda baik-baik saja, jika agak merendahkan. Saya membayangkan banyak rintangan yang harus dilewati oleh pengembang Java tampak konyol bagi pengembang .NET.
Outlaw Programmer
1
@Oscar, dia ingin mengkloning, dan tidak memanggil perintah clone. Mungkin tidak sama kalau klon tidak benar-benar klon. Saya pikir inilah intinya. Ini memang pertanyaan yang sulit.
Rafa

Jawaban:

62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();
Vinko Vrsalovic
sumber
43
Ini akan berfungsi dengan baik untuk Strings (yang merupakan pertanyaan yang ditanyakan), tetapi perlu dicatat bahwa ArrayList.clone akan melakukan salinan dangkal, jadi jika ada objek yang bisa berubah dalam daftar, mereka tidak akan dikloning (dan mengubah satu dalam satu daftar akan mengubah yang di daftar yang lain juga.
pkaeding
49
Anda harus menghindari penggunaan tipe mentah dalam apa pun kecuali kode warisan. Anda lebih baik menggunakan ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
cdmckay
20
Sayang sekali ArrayList memiliki metode #clone, tetapi List itu sendiri tidak. Mendesah.
rogerdpack
12
Tidak terkait tetapi saat itu: gunakan List<String>bukan ArrayList<String>di sisi kiri. Inilah cara koleksi harus digunakan sebagian besar waktu.
Christophe Roussy
3
Saya tidak bisa menggunakan metode ini untuk menduplikasi ArrayList. Metode clone () tidak dikenali.
Jack
318

Mengapa Anda ingin mengkloning? Membuat daftar baru biasanya lebih masuk akal.

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

Pekerjaan selesai.

Tom Hawtin - tackline
sumber
17
Anda mungkin tidak tahu apa jenis Daftar itu. Mungkin itu adalah LinkedList, MyOwnCustomList, atau subclass dari ArrayList, dalam hal ini memberikan ArrayList jenis yang salah.
Steve Kuo
51
Apakah saya peduli implementasi mana yang digunakan oleh daftar asli? Saya mungkin peduli implementasi mana yang digunakan daftar baru.
Tom Hawtin - tackline
12
@Steve Kuo: Tanda tangan ArrayList(Collection<? extends E> c)berarti tidak masalah apa pun daftar yang Anda gunakan sebagai argumen.
cdmckay
3
Maksud saya itu bukan salinan yang dalam, bukan?
4
@YekhezkelYovel Tidak, tidak akan.
Tom Hawtin - tackline
19

Ini adalah kode yang saya gunakan untuk itu:

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

Semoga bermanfaat bagi Anda

Jerman
sumber
3
Dan hindari menggunakan jenis mentah .. jadi alih-alih ArrayListgunakanArrayList<YourObject>
milosmns
2
docs.oracle.com/javase/8/docs/api/java/util/… Tidak berfungsi karena ukuran daftar berbeda.
MLProgrammer-CiM
Kenapa Anda tidak menggunakan copy overload saja dari ArrayListkonstruktor?
Tom Hawtin - tackline
19

Dengan Java 8 dapat diklon dengan stream.

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

...

List<AnObject> clone = myList.stream().collect(toList());
Simon Jenkins
sumber
Dapat dilakukan seperti Daftar <AnObject> xy = new ArrayList <> (oldList);
mirzak
1
Ini bukan salinan yang dalam, perubahan pada elemen satu daftar dapat dilihat di yang lain
Inchara
2
Di mana dalam pertanyaan yang ditentukan salinan dalam diperlukan? Asumsinya adalah bahwa koleksi lain yang berisi objek yang sama diperlukan. Jika Anda ingin pergi ke jalur copy yang dalam maka Anda membuka kaleng cacing yang sama sekali baru.
Simon Jenkins
Tapi bukan benar-benar tiruan, kan? Dari dokumen API "Tidak ada jaminan pada jenis, mutabilitas, serializability, atau keamanan thread dari Daftar yang dikembalikan". Euw, tidak mau salah satunya. toCollectionmungkin merupakan pilihan yang lebih baik.
Tom Hawtin - tackline
16

Maklum bahwa Object.clone () memiliki beberapa masalah besar, dan penggunaannya tidak disarankan dalam banyak kasus. Silakan lihat Butir 11, dari " Java Efektif " oleh Joshua Bloch untuk jawaban lengkap. Saya percaya Anda dapat menggunakan Object.clone () dengan aman pada array tipe primitif, tetapi selain itu Anda harus bijaksana dalam menggunakan dan meng-override clone secara benar. Anda mungkin lebih baik mendefinisikan konstruktor salinan atau metode pabrik statis yang secara eksplisit mengkloning objek sesuai dengan semantik Anda.

Julien Chastang
sumber
15

Saya pikir ini harus dilakukan dengan menggunakan API Koleksi:

Catatan : metode salin berjalan dalam waktu linier.

//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);
Harun
sumber
13
Mengapa tidak menggunakan ArrayList baru <String> (oldList)?
cdmckay
4
Saya percaya ini tidak akan berhasil, dari doc * Daftar tujuan harus setidaknya selama daftar sumber. Jika lebih lama, elemen yang tersisa di daftar tujuan tidak terpengaruh.
Greg Domjan
@ GregDomjan bukan berarti saya setuju bahwa ini adalah cara terbaik, tapi ini cara untuk melakukannya. Untuk mengatasi masalah Anda, ini sama seperti ini: Daftar <String> newList = new ArrayList <> (oldList.size ());
nckbrz
1
Tidak yakin apakah itu terkait dengan Java 8, tetapi bahkan ketika menentukan ukurannya, masih mendapatkan pengecualian IndexOutOfBoundsException: destination.size () <source.size (): 0 <2 pada List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); Tampaknya menggunakan copyList.addAll(original);adalah alternatif yang baik
Gene Bo
@ GenBo Ini tidak menentukan ukuran. Ini menentukan kapasitas, yang merupakan hal yang sangat berbeda. Sukacita intargumen misteri . Saya tidak tahu mengapa Anda ingin menggunakan metode statis yang tidak jelas ini, bukannya yang lama addAll.
Tom Hawtin - tackline
8

Saya menemukan menggunakan addAll berfungsi dengan baik.

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

tanda kurung digunakan daripada sintaksis generik

Allain Lalonde
sumber
5
Itu akan bekerja untuk Strings, tetapi tidak untuk objek yang bisa berubah. Anda juga ingin mengkloningnya.
jodonnell
Ya, pertanyaannya adalah untuk string. Dan dia memiliki masalah obat generik yang tidak terlalu suka barang casting dalam hal ini.
Allain Lalonde
Juga, ArrayList.clone hanya akan melakukan klon yang dangkal, jadi objek yang bisa berubah dalam daftar tidak akan dikloning menggunakan metode itu juga.
pkaeding
Itu seharusnya ArrayList <String> btw. Juga, Anda mungkin lebih baik menggunakan ArrayList <String> baru (asli) karena kurang menulis dan sama jelasnya.
cdmckay
4
Kenapa tidak pakai saja new ArrayList<String>(original)?
user102008
6

Jika Anda menginginkan ini agar dapat mengembalikan Daftar dalam pengambil, akan lebih baik untuk melakukannya:

ImmutableList.copyOf(list);
Uri Shalit
sumber
4

Untuk mengkloning antarmuka umum seperti java.util.ListAnda hanya perlu melakukan itu. di sini Anda adalah contoh:

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

Agak sulit, tetapi berfungsi, jika Anda terbatas untuk mengembalikan Listantarmuka, jadi siapa pun setelah Anda dapat mengimplementasikan daftar Anda kapan saja dia mau.

Saya tahu jawaban ini dekat dengan jawaban akhir, tetapi jawaban saya menjawab bagaimana melakukan semua itu saat Anda bekerja dengan- Listorangtua generik - tidakArrayList

Ahmed Hamdy
sumber
2
Anda berasumsi bahwa itu akan selalu menjadi ArrayList yang tidak benar
jucardi
@JuanCarlosDiaz akan, ini adalah pertanyaan yang diajukan, itu tentang ArrayList, jadi saya menjawabnya untuk ArrayList :)
Ahmed Hamdy
2

Berhati-hatilah saat mengkloning ArrayLists. Kloning di java adalah dangkal. Ini berarti bahwa itu hanya akan mengkloning Arraylist itu sendiri dan bukan anggotanya. Jadi jika Anda memiliki ArrayList X1 dan mengkloningnya ke X2 setiap perubahan dalam X2 juga akan bermanifestasi dalam X1 dan sebaliknya. Ketika Anda mengkloning, Anda hanya akan menghasilkan ArrayList baru dengan pointer ke elemen yang sama dalam aslinya.

Juan Besa
sumber
2

Ini juga harus bekerja:

ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()
pkaeding
sumber
2
List<String> shallowClonedList = new ArrayList<>(listOfStrings);

Perlu diingat bahwa ini hanya salinan dangkal bukan dalam, yaitu. Anda mendapatkan daftar baru, tetapi isinya sama. Ini bukan masalah untuk string saja. Lebih rumit ketika entri daftar adalah objek sendiri.

Robert
sumber
1
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();
Jodonnell
sumber
1

Saya bukan profesional java, tetapi saya memiliki masalah yang sama dan saya mencoba menyelesaikannya dengan metode ini. (Misalkan T memiliki copy constructor).

 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);
      }
}
Petr
sumber
Tidak ada jaminan bahwa Listkelas implementasi memiliki konstruktor no-arg publik. Misalnya, Listkembali oleh Array.asList, List.ofatau List.subListmungkin tidak.
Tom Hawtin - tackline