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>?


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

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.


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


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

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.


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

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


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

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


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.


2

Ini juga harus bekerja:

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

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.


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

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

Tidak ada jaminan bahwa Listkelas implementasi memiliki konstruktor no-arg publik. Misalnya, Listkembali oleh Array.asList, List.ofatau List.subListmungkin tidak.
Tom Hawtin - tackline
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.