ฉันจะโคลนรายการทั่วไปใน Java ได้อย่างไร


155

ฉันมีสิ่งArrayList<String>ที่ฉันต้องการคืนสำเนา ArrayListมีวิธีการโคลนซึ่งมีลายเซ็นดังต่อไปนี้:

public Object clone()

หลังจากที่ฉันเรียกเมธอดนี้ฉันจะเหวี่ยง Object ที่ส่งคืนกลับไปได้ArrayList<String>อย่างไร


16
ไม่นี่เป็นคำถามที่ถูกต้อง Java ไม่สนับสนุน generics "จริง" ด้วยการลบประเภท runtime และทั้งหมดดังนั้นรายละเอียดเหล่านี้อาจมีความยุ่งยาก นอกจากนี้อินเตอร์เฟส Cloneable และกลไกเมธอด Object.clone () ก็สร้างความสับสนในทำนองเดียวกัน
Outlaw Programmer

ตกลงฉันส่วนใหญ่ทำ C # ซึ่งเป็นเรื่องง่าย โปรดแจ้งให้เราทราบหากคุณต้องการให้ฉันลบความคิดเห็นจากคำถามนี้
Espo

1
คุณสามารถแสดงความคิดเห็น ฉันคิดว่าการแก้ไขของฉันอธิบายสิ่งที่ฉันมีปัญหา
Bill the Lizard

2
ความคิดเห็นของคุณก็โอเคถ้าวางตัวเล็กน้อย ฉันจินตนาการว่าห่วงจำนวนมากที่นักพัฒนา Java ต้องดูให้ดูเหมือนนักพัฒนา. NET ที่โง่เขลา
Outlaw Programmer

1
@Oscar เขาต้องการโคลนและไม่เรียกใช้คำสั่ง clone มันอาจจะไม่เหมือนกันที่โคลนไม่ได้โคลนจริงๆ ฉันคิดว่านี่เป็นประเด็น นี่เป็นคำถามที่ยุ่งยาก
Rafa

คำตอบ:


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

43
สิ่งนี้จะใช้งานได้ดีสำหรับ Strings (ซึ่งเป็นคำถามที่ถาม) แต่ก็น่าสังเกตว่า ArrayList.clone จะทำการคัดลอกตื้นดังนั้นหากมีวัตถุที่ไม่แน่นอนในรายการพวกเขาจะไม่ถูกโคลน (และเปลี่ยนอย่างใดอย่างหนึ่ง ในรายการหนึ่งจะเปลี่ยนหนึ่งในรายการอื่นเช่นกัน
pkaeding

49
คุณควรหลีกเลี่ยงการใช้ประเภท raw ในสิ่งใดนอกจากรหัสดั้งเดิม ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();คุณดีกว่าการใช้
cdmckay

20
มันเลวร้ายเกินไป ArrayList มีวิธีการ #clone แต่ List ไม่ได้ ถอนหายใจ
rogerdpack

12
ไม่เกี่ยวข้อง แต่ใช้งาน: ใช้List<String>แทนArrayList<String>ทางด้านซ้าย นี่คือวิธีการใช้คอลเลกชันเกือบตลอดเวลา
Christophe Roussy

3
ฉันไม่สามารถใช้วิธีนี้เพื่อทำซ้ำ ArrayList ไม่รู้จักวิธีการโคลน ()
แจ็ค

318

ทำไมคุณต้องการโคลน การสร้างรายการใหม่มักจะเหมาะสมกว่า

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

งานเสร็จแล้ว


17
คุณอาจไม่ทราบว่าเป็นรายการประเภทใด มันอาจจะเป็น LinkedList, MyOwnCustomList หรือคลาสย่อยของ ArrayList ซึ่งในกรณีที่การ ArrayList ใหม่จะเป็นประเภทที่ไม่ถูกต้อง
Steve Kuo

51
ฉันจะสนใจการใช้งานรายการดั้งเดิมที่ใช้หรือไม่ ฉันอาจสนใจที่การใช้งานรายการใหม่ใช้
Tom Hawtin - tackline

12
@Steve Kuo: ลายเซ็นมีArrayList(Collection<? extends E> c)ความหมายว่ามันไม่สำคัญว่าคุณใช้รายการประเภทใดเป็นอาร์กิวเมนต์
cdmckay

3
ฉันหมายความว่ามันไม่ใช่สำเนาที่ลึกซึ้งใช่ไหม

4
@YekhezkelYovel ไม่มันคงไม่ใช่
Tom Hawtin - tackline

19

นี่คือรหัสที่ฉันใช้:

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

หวังว่าจะเป็นประโยชน์สำหรับคุณ


3
และหลีกเลี่ยงการใช้ประเภทดิบ .. ดังนั้นแทนที่จะArrayListใช้ArrayList<YourObject>
milosmns

2
docs.oracle.com/javase/8/docs/api/java/util/… ไม่ทำงานเนื่องจากขนาดรายการแตกต่างกัน
MLProgrammer-CiM

ทำไมคุณไม่ใช้การคัดลอกมากเกินไปของArrayListนวกรรมิก?
Tom Hawtin - tackline

19

ด้วย Java 8 มันสามารถโคลนกับกระแส

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

...

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

สามารถทำได้เช่น List <AnObject> xy = new ArrayList <> (oldList);
mirzak

1
นี่ไม่ใช่การคัดลอกที่ลึกการเปลี่ยนแปลงองค์ประกอบของรายการหนึ่งสามารถเห็นได้ในอีกรายการหนึ่ง
Inchara

2
ในกรณีที่ระบุคำถามจำเป็นต้องมีสำเนาลึก สมมติฐานคือต้องการคอลเลกชันอื่นที่มีวัตถุเดียวกัน หากคุณต้องการลงเส้นทางการคัดลอกลึกคุณจะเปิดเวิร์มใหม่ทั้งหมด
Simon Jenkins

ไม่ใช่การโคลนนิ่งจริง ๆ ใช่ไหม? จากเอกสาร API "ไม่มีการรับประกันเกี่ยวกับประเภท, ความไม่แน่นอน, ความต่อเนื่องหรือความปลอดภัยของเธรดของรายการที่ส่งคืน" Euw ไม่ต้องการหนึ่งในนั้น toCollectionอาจเป็นทางเลือกที่ดีกว่า
Tom Hawtin - tackline

16

โปรดทราบว่า Object.clone () มีปัญหาที่สำคัญบางประการและการใช้งานนั้นไม่ได้รับการสนับสนุนในกรณีส่วนใหญ่ โปรดดูรายการที่ 11 จาก " Java ที่มีประสิทธิภาพ " โดย Joshua Bloch สำหรับคำตอบที่สมบูรณ์ ฉันเชื่อว่าคุณสามารถใช้ Object.clone () ในอาร์เรย์ประเภทดั้งเดิมได้อย่างปลอดภัย แต่นอกเหนือจากนั้นคุณจะต้องมีความรอบคอบในการใช้และการแทนที่โคลนอย่างถูกต้อง คุณน่าจะดีกว่าการกำหนดตัวสร้างการคัดลอกหรือวิธีการโรงงานคงที่ชัดเจนโคลนวัตถุตามความหมายของคุณ


15

ฉันคิดว่านี่น่าจะเป็นเคล็ดลับที่ใช้ Collections API:

หมายเหตุ : วิธีการคัดลอกทำงานในเวลาเชิงเส้น

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

13
ทำไมไม่ใช้ ArrayList <String> ใหม่ (oldList) ใหม่
cdmckay

4
ฉันเชื่อว่าสิ่งนี้จะใช้งานไม่ได้จาก doc * รายการปลายทางต้องมีอย่างน้อยตราบเท่าที่รายการแหล่ง หากยาวกว่าองค์ประกอบที่เหลือในรายการปลายทางจะไม่ได้รับผลกระทบ
เกร็ก Domjan

@ GregDomjan ไม่ใช่ว่าฉันยอมรับว่านี่เป็นวิธีที่ดีที่สุด แต่เป็นวิธีที่จะทำ เพื่อแก้ไขปัญหาของคุณมันเป็นเรื่องง่ายเหมือนนี้: List <String> newList = new ArrayList <> (oldList.size ());
nckbrz

1
ไม่แน่ใจว่าเกี่ยวข้องกับ Java 8 หรือไม่แม้ว่าจะเป็นการระบุขนาด แต่ก็ยังได้รับข้อยกเว้นIndexOutOfBoundsException: destination.size () <source.size (): 0 source.size (): 0 <2บนList<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); ดูเหมือนว่าการใช้copyList.addAll(original);เป็นทางเลือกที่ดี
Gene Bo

@GeneBo มันไม่ได้ระบุขนาด มันกำลังระบุความจุซึ่งแตกต่างกันมาก ความสุขของการintขัดแย้งลึกลับ addAllผมเคยคิดว่าทำไมคุณต้องการที่จะใช้วิธีการนี้คงปิดบังแทนดีเก่า
Tom Hawtin - tackline

8

ฉันพบว่าการใช้ addAll ใช้งานได้ดี

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

วงเล็บถูกใช้มากกว่าไวยากรณ์ทั่วไป


5
มันจะใช้ได้กับ Strings แต่ไม่ใช่สำหรับวัตถุที่ไม่แน่นอน คุณต้องการโคลนเหล่านั้นด้วย
jodonnell

ใช่คำถามของเขาสำหรับสาย และเขามีปัญหาเรื่องยาชื่อสามัญซึ่งไม่ชอบสิ่งที่หล่อในกรณีนี้
Allain Lalonde

นอกจากนี้ ArrayList.clone จะทำการโคลนแบบโคลนเท่านั้นดังนั้นวัตถุที่ไม่แน่นอนในรายการจะไม่ถูกโคลนโดยใช้วิธีการนั้น
pkaeding

นั่นควรเป็น ArrayList <String> btw นอกจากนี้คุณน่าจะดีกว่าการใช้ ArrayList <String> ใหม่ (ต้นฉบับ) เนื่องจากมีการเขียนน้อยลงและชัดเจนขึ้น
cdmckay

4
ทำไมไม่ใช้เพียงnew ArrayList<String>(original)?
user102008


4

หากต้องการโคลนอินเทอร์เฟซทั่วไปอย่างjava.util.Listคุณจะต้องส่งมัน นี่คือตัวอย่าง:

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

มันค่อนข้างยุ่งยาก แต่ใช้งานได้ถ้าคุณถูก จำกัด ให้ส่งคืนListอินเทอร์เฟซดังนั้นใครก็ตามหลังจากคุณสามารถใช้รายการของคุณได้ทุกเมื่อที่เขาต้องการ

ฉันรู้ว่าคำตอบนี้อยู่ใกล้กับคำตอบสุดท้าย แต่คำตอบของฉันตอบว่าจะทำอย่างไรในขณะที่คุณทำงานกับList- ผู้ปกครองทั่วไป - ไม่ArrayList


2
คุณกำลังสันนิษฐานว่ามันจะเป็นรายการ ArrayList ซึ่งไม่เป็นความจริง
เสมอ

@JuanCarlosDiaz จะนี้เป็นคำถามที่ถามมันเป็นเรื่อง ArrayList ดังนั้นผมตอบมันสำหรับ ArrayList :)
อาเหม็ด Hamdy

2

ระวังตัวมากเมื่อทำการโคลน ArrayLists การโคลนนิ่งใน java เป็นแบบตื้น นี่หมายความว่ามันจะโคลน Arraylist เท่านั้นเองและไม่ใช่สมาชิกของ Arraylist ดังนั้นหากคุณมี ArrayList X1 และโคลนลงใน X2 การเปลี่ยนแปลงใด ๆ ใน X2 ก็จะปรากฏใน X1 และในทางกลับกัน เมื่อคุณโคลนคุณจะสร้าง ArrayList ใหม่ที่มีตัวชี้ไปยังองค์ประกอบเดียวกันในต้นฉบับ



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

โปรดจำไว้ว่านี่เป็นเพียงสำเนาตื้น ๆ เท่านั้น คุณได้รับรายการใหม่ แต่รายการเหมือนกัน นี่ไม่ใช่ปัญหาสำหรับสตริงเพียงอย่างเดียว รับยุ่งยากมากขึ้นเมื่อรายการรายการเป็นวัตถุเอง



1

ฉันไม่ใช่จาวามืออาชีพ แต่ฉันมีปัญหาเดียวกันและฉันพยายามแก้ไขด้วยวิธีนี้ (สมมติว่า T มีตัวสร้างสำเนา)

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

ไม่มีการรับประกันว่าListคลาสการใช้งานจะมีตัวสร้างแบบไม่มีอาร์กิวเมนต์แบบสาธารณะ ยกตัวอย่างเช่นLists ผลตอบแทนโดยArray.asList, List.ofหรือList.subListอาจจะทำไม่ได้
Tom Hawtin - tackline
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.