ฉันจะโคลนArrayList
และโคลนรายการใน Java ได้อย่างไร
เช่นฉันมี:
ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....
และฉันคาดหวังว่าวัตถุในclonedList
นั้นจะไม่เหมือนกับรายการสุนัข
ฉันจะโคลนArrayList
และโคลนรายการใน Java ได้อย่างไร
เช่นฉันมี:
ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....
และฉันคาดหวังว่าวัตถุในclonedList
นั้นจะไม่เหมือนกับรายการสุนัข
คำตอบ:
คุณจะต้องวนซ้ำในไอเท็มและทำการโคลนนิ่งทีละตัววางโคลนนิ่งในอาร์เรย์ผลลัพธ์ของคุณในขณะที่คุณไป
public static List<Dog> cloneList(List<Dog> list) {
List<Dog> clone = new ArrayList<Dog>(list.size());
for (Dog item : list) clone.add(item.clone());
return clone;
}
เพื่อให้สามารถใช้งานได้อย่างชัดเจนคุณจะต้องทำให้Dog
คลาสของคุณใช้Cloneable
อินเทอร์เฟซและแทนที่clone()
เมธอด
โดยส่วนตัวแล้วฉันจะเพิ่มนวกรรมิกใน Dog:
class Dog
{
public Dog()
{ ... } // Regular constructor
public Dog(Dog dog) {
// Copy all the fields of Dog.
}
}
จากนั้นแค่ทำซ้ำ (ดังแสดงในคำตอบของ Varkhan):
public static List<Dog> cloneList(List<Dog> dogList) {
List<Dog> clonedList = new ArrayList<Dog>(dogList.size());
for (Dog dog : dogList) {
clonedList.add(new Dog(dog));
}
return clonedList;
}
ฉันพบว่าข้อดีของสิ่งนี้คือคุณไม่จำเป็นต้องไปไหนมาไหนกับสิ่งที่ Cloneable ใน Java นอกจากนี้ยังตรงกับวิธีที่คุณคัดลอกคอลเลกชัน Java
ตัวเลือกอื่นอาจเป็นการเขียนส่วนต่อประสาน ICloneable ของคุณเองและใช้สิ่งนั้น ด้วยวิธีนี้คุณสามารถเขียนวิธีทั่วไปสำหรับการโคลน
cloneList(List<Object>)
หรือDog(Object)
?
คอลเลกชันมาตรฐานทั้งหมดมีตัวสร้างสำเนา ใช้มัน.
List<Double> original = // some list
List<Double> copy = new ArrayList<Double>(original); //This does a shallow copy
clone()
ได้รับการออกแบบโดยมีข้อผิดพลาดหลายประการ (ดูคำถามนี้ ) ดังนั้นจึงเป็นการดีที่สุดที่จะหลีกเลี่ยง
จากEffective Java 2nd Edition , Item 11: Override clone อย่างรอบคอบ
เนื่องจากปัญหาทั้งหมดที่เกี่ยวข้องกับ Cloneable นั้นปลอดภัยที่จะกล่าวว่าอินเทอร์เฟซอื่นไม่ควรขยายและคลาสที่ออกแบบมาสำหรับการสืบทอด (รายการที่ 17) ไม่ควรนำมาใช้ เนื่องจากข้อบกพร่องจำนวนมากโปรแกรมเมอร์ผู้เชี่ยวชาญบางคนก็เลือกที่จะไม่แทนที่วิธีการโคลนและไม่เรียกมันยกเว้นบางทีเพื่อคัดลอกอาร์เรย์ หากคุณออกแบบคลาสสำหรับการสืบทอดโปรดทราบว่าหากคุณเลือกที่จะไม่ให้วิธีการโคลนที่มีการป้องกันอย่างดีมันจะเป็นไปไม่ได้ที่คลาสย่อยจะใช้ Cloneable
หนังสือเล่มนี้ยังอธิบายถึงข้อดีของการสร้างสำเนาที่มีมากกว่า Cloneable / clone
พิจารณาประโยชน์ของการใช้การก่อสร้างสำเนาอื่น: สมมติว่าคุณมีและคุณต้องการคัดลอกเป็นHashSet s
TreeSet
วิธีโคลนไม่สามารถนำเสนอฟังก์ชั่นนี้ new TreeSet(s)
แต่มันเป็นเรื่องง่ายด้วยตัวสร้างแปลง:
Java 8 มีวิธีการใหม่ที่จะเรียกตัวสร้างสำเนาหรือวิธีการโคลนสุนัของค์ประกอบอย่างหรูหราและดาน: Streams , lambdasและนักสะสม
คัดลอกคอนสตรัค:
List<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toList());
การแสดงออกDog::new
ที่เรียกว่าการอ้างอิงวิธี มันสร้างฟังก์ชั่นวัตถุที่เรียกคอนสตรัคเตอร์Dog
ซึ่งใช้สุนัขตัวอื่นเป็นอาร์กิวเมนต์
วิธีการโคลน [1]:
List<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toList());
ArrayList
ผลที่ตามมาหรือถ้าคุณต้องรับArrayList
กลับ (ในกรณีที่คุณต้องการแก้ไขในภายหลัง):
ArrayList<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toCollection(ArrayList::new));
หากคุณไม่จำเป็นต้องเก็บเนื้อหาดั้งเดิมของdogs
รายการไว้คุณสามารถใช้replaceAll
วิธีการและอัปเดตรายการแทน
dogs.replaceAll(Dog::new);
import static java.util.stream.Collectors.*;
ตัวอย่างทั้งหมดถือว่า
ArrayList
sตัวรวบรวมจากตัวอย่างล่าสุดสามารถทำเป็นวิธีใช้ได้ เนื่องจากนี่เป็นสิ่งที่ธรรมดามากที่ฉันจะทำเองสั้นและสวย แบบนี้:
ArrayList<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toArrayList());
public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
return Collectors.toCollection(ArrayList::new);
}
CloneNotSupportedException
:สำหรับการแก้ปัญหานี้ในการทำงานclone
วิธีการDog
ไม่ต้องCloneNotSupportedException
ประกาศว่ามันจะพ่น เหตุผลคืออาร์กิวเมนต์map
ไม่ได้รับอนุญาตให้โยนข้อยกเว้นที่ตรวจสอบแล้ว
แบบนี้:
// Note: Method is public and returns Dog, not Object
@Override
public Dog clone() /* Note: No throws clause here */ { ...
นี่ไม่ควรเป็นปัญหาใหญ่เพราะเป็นวิธีปฏิบัติที่ดีที่สุดอยู่ดี ( เช่น Effectice Javaให้คำแนะนำนี้)
ขอบคุณ Gustavo ที่สังเกตสิ่งนี้
หากคุณพบว่าน่าสนใจคุณสามารถใช้ไวยากรณ์การอ้างอิงเมธอดแทนเพื่อทำสิ่งเดียวกัน:
List<Dog> clonedDogs = dogs.stream().map(Dog::clone).collect(toList());
List<Dog> clonedDogs = new ArrayList<>(); dogs.stream().parallel().forEach(d -> clonedDogs.add(new Dog(d)));
parallel
โทรทำให้clonedDogs.add
ถูกเรียกจากหลายเธรดในเวลาเดียวกัน เวอร์ชันที่ใช้collect
นั้นปลอดภัยต่อเธรด นี่เป็นหนึ่งในข้อดีของฟังก์ชั่นการทำงานของไลบรารี่ไลบรารี่สามารถใช้โค้ดเดียวกันสำหรับสตรีมแบบขนาน
Unhandled exception type CloneNotSupportedException
d.clone()
การประกาศข้อยกเว้นหรือการจับไม่ได้ช่วยแก้ปัญหา
Dog
ในตัวอย่างนี้) ไม่รองรับการโคลน คุณแน่ใจหรือว่าใช้Clonable
อินเทอร์เฟซได้
โดยทั่วไปมีสามวิธีโดยไม่ต้องวนซ้ำด้วยตนเอง
1 ใช้ตัวสร้าง
ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>(dogs);
2 การใช้ addAll(Collection<? extends E> c)
ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(dogs);
3 ใช้addAll(int index, Collection<? extends E> c)
วิธีการที่มีint
พารามิเตอร์
ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(0, dogs);
หมายเหตุ: พฤติกรรมของการดำเนินการเหล่านี้จะไม่ได้กำหนดหากมีการแก้ไขการรวบรวมที่ระบุในขณะที่การดำเนินการอยู่ในระหว่างดำเนินการ
ฉันคิดว่าคำตอบสีเขียวปัจจุบันไม่ดีทำไมคุณอาจถาม
วิธีการทำให้เป็นอันดับเป็น imo ไม่ดีคุณอาจต้องเพิ่ม Serializable ให้ทั่ว
ดังนั้นทางออกคืออะไร:
ไลบรารี Java Deep-Cloning ไลบรารี โคลนเป็นไลบรารี java ขนาดเล็กแบบโอเพ่นซอร์ส (apache license) ซึ่งเป็นวัตถุโคลนนิ่งลึก วัตถุไม่จำเป็นต้องใช้ส่วนต่อประสาน Cloneable Effectivelly ห้องสมุดนี้สามารถโคลนวัตถุ java ใด ๆ สามารถใช้งานได้เช่นในการปรับใช้แคชหากคุณไม่ต้องการให้วัตถุแคชถูกแก้ไขหรือเมื่อใดก็ตามที่คุณต้องการสร้างสำเนาวัตถุ
Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);
ตรวจสอบได้ที่https://github.com/kostaskougios/cloning
ฉันพบวิธีคุณสามารถใช้ json เพื่อทำให้เป็นอนุกรม / unserialize รายการ รายการต่อเนื่องไม่มีการอ้างอิงไปยังวัตถุต้นฉบับเมื่อไม่ได้ระบุ
ใช้ gson:
List<CategoryModel> originalList = new ArrayList<>(); // add some items later
String listAsJson = gson.toJson(originalList);
List<CategoryModel> newList = new Gson().fromJson(listAsJson, new TypeToken<List<CategoryModel>>() {}.getType());
คุณสามารถทำได้โดยใช้แจ็คสันและห้องสมุด json อื่น ๆ ด้วย
ฉันใช้ตัวเลือกนี้เสมอ:
ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone);
คุณจะต้องโคลนArrayList
ด้วยมือ (โดยวนซ้ำและคัดลอกแต่ละองค์ประกอบไปยังองค์ประกอบใหม่ArrayList
) เพราะclone()
จะไม่ทำเพื่อคุณ เหตุผลสำหรับสิ่งนี้คือวัตถุที่มีอยู่ในArrayList
อาจไม่ใช้งานClonable
เอง
แก้ไข : ... และนั่นคือสิ่งที่รหัสของ Varkhan ทำ
วิธีที่น่ารังเกียจคือการทำด้วยการสะท้อน บางอย่างเช่นนี้ใช้ได้สำหรับฉัน
public static <T extends Cloneable> List<T> deepCloneList(List<T> original) {
if (original == null || original.size() < 1) {
return new ArrayList<>();
}
try {
int originalSize = original.size();
Method cloneMethod = original.get(0).getClass().getDeclaredMethod("clone");
List<T> clonedList = new ArrayList<>();
// noinspection ForLoopReplaceableByForEach
for (int i = 0; i < originalSize; i++) {
// noinspection unchecked
clonedList.add((T) cloneMethod.invoke(original.get(i)));
}
return clonedList;
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
System.err.println("Couldn't clone list due to " + e.getMessage());
return new ArrayList<>();
}
}
original
มีวัตถุของคลาสที่แตกต่างกันฉันคิดว่าcloneMethod.invoke
จะล้มเหลวโดยมีข้อยกเว้นเมื่อมีการเรียกใช้ด้วยวัตถุชนิดที่ไม่ถูกต้อง ด้วยเหตุนี้จึงเป็นการดีกว่าที่จะดึงโคลนเฉพาะMethod
สำหรับแต่ละวัตถุ หรือใช้วิธีการโคลนบนObject
(แต่เนื่องจากมีการป้องกันที่อาจล้มเหลวในกรณีอื่น ๆ )
List<Dog> dogs;
List<Dog> copiedDogs = dogs.stream().map(dog -> SerializationUtils.clone(dog)).Collectors.toList());
นี่จะคัดลอกสุนัขแต่ละตัวอย่างลึกซึ้ง
ผู้โพสต์อื่นถูกต้อง: คุณต้องวนซ้ำรายการและคัดลอกไปยังรายการใหม่
อย่างไรก็ตาม ... หากวัตถุในรายการไม่สามารถเปลี่ยนแปลงได้ - คุณไม่จำเป็นต้องทำการโคลน หากวัตถุของคุณมีกราฟวัตถุที่ซับซ้อนพวกเขาจะต้องไม่เปลี่ยนรูปเช่นกัน
ประโยชน์อื่น ๆ ของการเปลี่ยนรูปไม่ได้ก็คือพวกมันมีความปลอดภัยเช่นกัน
นี่คือวิธีแก้ไขโดยใช้ประเภทเทมเพลตทั่วไป:
public static <T> List<T> copyList(List<T> source) {
List<T> dest = new ArrayList<T>();
for (T item : source) { dest.add(item); }
return dest;
}
สำหรับคุณวัตถุแทนที่โคลน () วิธีการ
class You_class {
int a;
@Override
public You_class clone() {
You_class you_class = new You_class();
you_class.a = this.a;
return you_class;
}
}
และโทร. clone () สำหรับ Vector obj หรือ ArraiList obj ....
วิธีง่ายๆโดยใช้ commons-lang-2.3.jar ไลบรารีของ java ไปยังรายการโคลน
ลิงค์ดาวน์โหลด commons-lang-2.3.jar
วิธีใช้
oldList.........
List<YourObject> newList = new ArrayList<YourObject>();
foreach(YourObject obj : oldList){
newList.add((YourObject)SerializationUtils.clone(obj));
}
ฉันหวังว่าอันนี้จะมีประโยชน์
: D
แพคเกจ import org.apache.commons.lang.SerializationUtils;
มีวิธีการคือ SerializationUtils.clone(Object);
ตัวอย่าง
this.myObjectCloned = SerializationUtils.clone(this.object);
ฉันเพิ่งพัฒนา lib ที่สามารถโคลนเอนทิตีวัตถุและวัตถุ java.util.List เพียงดาวน์โหลด jar ในhttps://drive.google.com/open?id=0B69Sui5ah93EUTloSktFUkctN0Uและใช้ cloneListObject (รายการรายการ) วิธีนี้ไม่เพียง แต่โคลนรายการ แต่ยังรวมถึงองค์ประกอบเอนทิตีทั้งหมด
ด้านล่างใช้งานได้สำหรับฉัน ..
ใน Dog.java
public Class Dog{
private String a,b;
public Dog(){} //no args constructor
public Dog(Dog d){ // copy constructor
this.a=d.a;
this.b=d.b;
}
}
-------------------------
private List<Dog> createCopy(List<Dog> dogs) {
List<Dog> newDogsList= new ArrayList<>();
if (CollectionUtils.isNotEmpty(dogs)) {
dogs.stream().forEach(dog-> newDogsList.add((Dog) SerializationUtils.clone(dog)));
}
return newDogsList;
}
นี่คือรายการใหม่ที่สร้างขึ้นจากเมธอด createCopy ถูกสร้างผ่าน SerializationUtils.clone () ดังนั้นการเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นกับรายการใหม่จะไม่มีผลกับรายการต้นฉบับ
ฉันคิดว่าฉันพบวิธีที่ง่ายมากในการทำ ArrayList ที่เป็นสำเนา สมมติว่าคุณต้องการคัดลอก String ArrayList arrayA
ArrayList<String>arrayB = new ArrayList<String>();
arrayB.addAll(arrayA);
แจ้งให้เราทราบหากไม่ได้ผลสำหรับคุณ