สำเนา Java ArrayList


214

ฉันมีArrayList l1ขนาด 10 ฉันกำหนดประเภทอ้างอิงรายการใหม่l1 l2จะl1และl2ชี้ไปที่ArrayListวัตถุเดียวกันหรือไม่ หรือเป็นสำเนาของArrayListวัตถุที่กำหนดให้l2?

เมื่อใช้การl2อ้างอิงถ้าฉันอัปเดตวัตถุรายการมันสะท้อนถึงการเปลี่ยนแปลงในl1ประเภทการอ้างอิงด้วย

ตัวอย่างเช่น:

List<Integer> l1 = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++) {
    l1.add(i);
}

List l2 = l1;
l2.clear();

ไม่มีวิธีอื่นในการกำหนดสำเนาของวัตถุรายการให้กับตัวแปรอ้างอิงใหม่นอกเหนือจากการสร้างวัตถุ 2 รายการและทำสำเนาบนคอลเล็กชันจากเก่าไปใหม่หรือไม่

คำตอบ:


460

ใช่เพียงแค่ได้รับมอบหมายจะคัดลอกค่าของl1(ซึ่งเป็นการอ้างอิง) l2เพื่อ พวกเขาทั้งสองจะอ้างถึงวัตถุเดียวกัน

การสร้างสำเนาตื้น ๆ นั้นค่อนข้างง่ายแม้ว่า:

List<Integer> newList = new ArrayList<>(oldList);

(เช่นเดียวกับตัวอย่าง)


1
เป็นไปได้หรือไม่ที่จะคัดลอกเฉพาะบางส่วนของ arraylist ไปยัง arraylist ใหม่อย่างมีประสิทธิภาพ สำหรับตัวอย่าง: คัดลอกองค์ประกอบระหว่างตำแหน่ง 5 ถึง 10 จากหนึ่งอาเรย์ลิสต์ไปยังอาเรย์ลิสต์ใหม่อีกรายการ ในแอปพลิเคชันของฉันช่วงจะใหญ่กว่ามาก
Ashwin

1
@Ashwin: มันเป็นการดำเนินการ O (N) แต่ใช่ ... คุณสามารถใช้List.subListเพื่อให้ได้ "มุมมอง" ไปยังส่วนของรายการต้นฉบับ
Jon Skeet

เกิดอะไรขึ้นถ้ารายการอาร์เรย์ซ้อนกัน(ArrayList<ArrayList<Object>>)? สิ่งนี้จะสร้างสำเนาของวัตถุ ArrayList สำหรับเด็กทั้งหมดซ้ำหรือไม่
Cat

3
@Cat: ไม่ ... นี่เป็นเพียงสำเนาตื้น ๆ
Jon Skeet

1
@ShanikaEdiriweera: คุณสามารถทำได้อย่างคล่องแคล่วด้วยลำธารใช่ แต่ส่วนที่ยุ่งยากคือการสร้างสำเนาที่ลึกซึ่งวัตถุส่วนใหญ่ไม่ได้จัดเตรียมไว้ให้ หากคุณมีกรณีเฉพาะผมขอแนะนำให้คุณถามคำถามใหม่พร้อมรายละเอียด
Jon Skeet

67

ลองใช้ดู Collections.copy(destination, source);


14
สนใจที่จะอธิบายว่าทำไมนี้อาจจะดีกว่าที่จะnew ArrayList<>(source);?
Alex

6
@atc มันเป็นอีกหนึ่งวิธีที่จะทำสำเนาตื้นแทน ArrayList () ใหม่ก็ใช้อัลกอริทึมอื่นและสามารถนำมาใช้สำหรับการดำเนินงานรายการใด ๆ ที่ไม่ได้เป็นเพียงการ ArrayList ที่เป็น :) ทั้งหมด
Sergii Zagriichuk

14
วิธีนี้ทำให้เข้าใจผิดมาก! จริงๆแล้วมันเป็นคำอธิบาย มันบอกว่า: "คัดลอกองค์ประกอบจากรายการแหล่งหนึ่งไปยังปลายทาง" แต่ไม่ได้คัดลอก! พวกเขาถูกอ้างอิงดังนั้นจะมีเพียง 1 สำเนาของวัตถุและหากพวกเขาไม่แน่นอนคุณกำลังมีปัญหา
ACV

5
ไม่มีที่ไหนใน java-api การโคลนนิ่งลึกทำได้โดยคลาสการรวบรวมใด ๆ
Vikash

คำตอบนี้ไม่สมเหตุสมผลนัก Collections.copyไม่ใช่ทางเลือกnew ArrayList<>(source)ทั้งหมด สิ่งที่Collections.copyจริง ๆ แล้วก็คือสมมติว่าdestination.size()อย่างน้อยก็ใหญ่source.size()แล้วคัดลอกช่วงดัชนี - ดัชนีโดยใช้set(int,E)วิธีการ วิธีนี้ไม่ได้เพิ่มองค์ประกอบใหม่ไปยังปลายทาง อ้างถึงซอร์สโค้ดหากยังไม่ชัดเจนพอจาก Javadoc
Radiodef

35

ใช่l1และl2จะชี้ไปที่การอ้างอิงเดียวกันวัตถุเดียวกัน

หากคุณต้องการสร้าง ArrayList ใหม่โดยยึดตาม ArrayList อื่นให้ทำดังนี้

List<String> l1 = new ArrayList<String>();
l1.add("Hello");
l1.add("World");
List<String> l2 = new ArrayList<String>(l1); //A new arrayList.
l2.add("Everybody");

ผลลัพธ์จะl1ยังคงมี 2 องค์ประกอบและl2จะมี 3 องค์ประกอบ


คุณช่วยอธิบายความแตกต่างระหว่างList<String> l2 = new ArrayList<String>(l1)และList<String> l2 = l1?
MortalMan

@MortalMan ความแตกต่างก็คือ l2 = ArrayList ใหม่ <String> (l1) เป็นวัตถุใหม่ทั้งหมดและการปรับเปลี่ยน l2 ไม่ส่งผลกระทบต่อ l1 ในขณะที่รายการ <String> l2 = l1 คุณไม่ได้สร้างวัตถุใหม่ แต่เพียงอ้างอิงเดียวกัน วัตถุเป็น l1 ดังนั้นในกรณีนี้ทำการดำเนินการเช่น l2.add ("ทุกคน"), l1.size () และ l2.size () จะกลับ 3 เพราะทั้งคู่จะอ้างอิงวัตถุเดียวกัน
Alfredo Osorio

19

อีกวิธีที่สะดวกในการคัดลอกค่าจาก src ArrayList ไปยัง Arraylist ปลายทางจะเป็นดังนี้:

ArrayList<String> src = new ArrayList<String>();
src.add("test string1");
src.add("test string2");
ArrayList<String> dest= new ArrayList<String>();
dest.addAll(src);

นี่คือการคัดลอกค่าจริงไม่ใช่แค่คัดลอกข้อมูลอ้างอิง


10
ฉันไม่แน่ใจว่าทั้งหมดนี้ถูกต้อง การทดสอบของฉันแสดงสิ่งที่ตรงกันข้าม (ยังคงอ้างอิงวัตถุเดียวกัน)
invertigo

วิธีนี้ใช้ได้กับฉันเมื่อใช้ ArrayList กับ ArrayAdapter
albanx

1
คำตอบนี้ผิด addAll () เพียงแค่คัดลอกข้อมูลอ้างอิงตาม invertigo กล่าว นี่ไม่ใช่สำเนาที่ลึกซึ้ง
jk7

สำหรับ ArrayList <String> คำตอบนี้ยอมรับได้เพราะ String นั้นไม่เปลี่ยนรูป แต่ลองใช้กับตัวอย่างของ OP คือ ArraList <Integer> และคุณจะเห็นว่ามันเป็นเพียงการคัดลอกข้อมูลอ้างอิง
jk7

ไม่ใช่วันของฉันฉันเดา มันกลับกลายเป็นคลาสเช่น Integer และ Long ก็ไม่เปลี่ยนรูปแบบดังนั้นคำตอบของ Harshal นั้นใช้ได้กับกรณีง่าย ๆ เช่น ArrayList <Integer> และ ArrayList <String> ที่ใดที่ล้มเหลวสำหรับวัตถุที่ซับซ้อนที่ไม่เปลี่ยนรูป
jk7

8

มีวิธีการaddAll ()ซึ่งจะให้บริการวัตถุประสงค์ของการคัดลอกหนึ่ง ArrayList ไปยังอีก

ตัวอย่างเช่นคุณมี Array สองรายการ: sourceListและtargetListใช้โค้ดด้านล่าง

targetList.addAll (SourceList);


มันก็แค่คัดลอกข้อมูลอ้างอิง
Vikash

4

Java ไม่ผ่านวัตถุมันผ่านการอ้างอิง (ตัวชี้) ไปยังวัตถุ ดังนั้นใช่ l2 และ l1 เป็นตัวชี้สองตัวสำหรับวัตถุเดียวกัน

คุณต้องทำสำเนาอย่างชัดเจนหากคุณต้องการสองรายการที่แตกต่างที่มีเนื้อหาเดียวกัน


3
คุณสร้าง "สำเนาที่ชัดเจน" ได้อย่างไร ฉันคิดว่าคุณกำลังพูดถึงสำเนาลึก ๆ ?
Cin316

1

List.copyOf list รายการที่เปลี่ยนแปลงไม่ได้

คุณถาม:

ไม่มีวิธีอื่นในการกำหนดสำเนาของรายการ

Java 9 นำList.ofวิธีการสำหรับการใช้ตัวอักษรเพื่อสร้างListคลาสที่ไม่รู้จักที่ไม่สามารถแยกแยะได้

LocalDate today = LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) ;
List< LocalDate > dates = List.of( 
    today.minusDays( 1 ) ,  // Yesterday
    today ,                 // Today
    today.plusDays( 1 )     // Tomorrow
);

List.copyOfพร้อมกับที่เรายังมี วิธีการนี้จะส่งกลับค่าที่ไม่สามารถแก้ไขได้Listของคลาสคอนกรีตที่ไม่รู้จัก

List< String > colors = new ArrayList<>( 4 ) ;          // Creates a modifiable `List`. 
colors.add ( "AliceBlue" ) ;
colors.add ( "PapayaWhip" ) ;
colors.add ( "Chartreuse" ) ;
colors.add ( "DarkSlateGray" ) ;
List< String > masterColors = List.copyOf( colors ) ;   // Creates an unmodifiable `List`.

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

colors.remove( 2 ) ;          // SUCCEEDS. 
masterColors.remove( 2 ) ;    // FAIL - ERROR.

ดูนี้ใช้รหัสสดที่ IdeOne.com

date.toString (): [2020-02-02, 2020-02-03, 2020-02-04]

colors.toString (): [AliceBlue, PapayaWhip, DarkSlateGray]

masterColors.toString (): [AliceBlue, PapayaWhip, Chartreuse, DarkSlateGray]

คุณถามถึงการอ้างอิงวัตถุ อย่างที่คนอื่นพูดถ้าคุณสร้างหนึ่งรายการและกำหนดให้กับตัวแปรอ้างอิงสองตัว (พอยน์เตอร์) คุณยังมีเพียงหนึ่งรายการ ทั้งสองชี้ไปที่รายการเดียวกัน หากคุณใช้ตัวชี้อย่างใดอย่างหนึ่งเพื่อปรับเปลี่ยนรายการตัวชี้ทั้งสองจะเห็นการเปลี่ยนแปลงในภายหลังเนื่องจากมีเพียงหนึ่งรายการในหน่วยความจำ

ดังนั้นคุณต้องทำสำเนาของรายการ หากคุณต้องการให้สำเนานั้นไม่สามารถแก้ไขได้ให้ใช้List.copyOfวิธีการตามที่อธิบายไว้ในคำตอบนี้ ในวิธีการนี้คุณจะมีสองรายการแยกกันแต่ละรายการมีองค์ประกอบที่อ้างอิงถึงวัตถุเนื้อหาเดียวกัน ตัวอย่างเช่นในตัวอย่างของเราด้านบนการใช้Stringวัตถุเพื่อแสดงสีวัตถุสีจะลอยไปมาในหน่วยความจำที่ไหนซักแห่ง รายการทั้งสองถือตัวชี้ไปยังวัตถุสีเดียวกัน นี่คือแผนภาพ

ป้อนคำอธิบายรูปภาพที่นี่

รายการแรกcolorsสามารถแก้ไขได้ ซึ่งหมายความว่าองค์ประกอบบางอย่างสามารถลบออกได้ตามที่เห็นในโค้ดด้านบนซึ่งเราลบองค์ประกอบที่ 3 ดั้งเดิมChartreuse(ดัชนี 2 = ลำดับ 3) และสามารถเพิ่มองค์ประกอบ และองค์ประกอบสามารถเปลี่ยนเป็นชี้ไปที่อื่น ๆStringเช่นOliveDrabหรือCornflowerBlueหรือ

ในทางตรงกันข้ามองค์ประกอบทั้งสี่ของmasterColorsได้รับการแก้ไข ไม่มีการลบไม่เพิ่มและไม่มีการแทนที่สีอื่น ว่าListการดำเนินการเป็น unmodifiable

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.