Answers:
您将需要迭代这些项目,并逐个克隆它们,然后将克隆放入结果数组中。
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;
}
我发现这样做的好处是您无需费心处理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,第11条:明智地覆盖克隆
鉴于与Cloneable相关的所有问题,可以肯定地说其他接口不应该扩展它,而为继承而设计的类(第17项)也不应实现它。由于它的许多缺点,一些专业的程序员只是选择永不覆盖clone方法,也不要调用它,除非复制数组。如果设计一个用于继承的类,请注意,如果选择不提供行为良好的受保护的克隆方法,则子类将无法实现Cloneable。
本书还描述了复制构造函数相对于Cloneable / clone的许多优点。
考虑使用复制构造函数的另一个好处:假设您有一个HashSet s
,并且想要将其复制为一个TreeSet
。clone方法无法提供此功能,但是使用转化构造函数即可轻松实现new TreeSet(s)
。
Java 8提供了一种新的方法来优雅而紧凑地调用元素狗上的copy构造函数或clone方法:Streams,lambdas和collectors。
复制构造函数:
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
小号上一个示例中的收集器可以做成util方法。由于这是一件很常见的事情,因此我个人希望它短而漂亮。像这样:
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
。原因是to的参数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
get可以同时从多个线程被调用。使用的版本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深度 克隆库克隆库是一个小型的,开源的(Apache许可证)Java库,用于深度克隆对象。对象不必实现Cloneable接口。实际上,该库可以克隆任何java对象。如果您不希望修改缓存的对象,或者想要创建对象的深层副本,则可以在缓存实现中使用它。
Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);
我找到了一种方法,您可以使用json对列表进行序列化/反序列化。未序列化时,序列化列表不包含对原始对象的引用。
使用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());
您也可以使用jackson和其他任何json库来实现。
讨厌的方法是反射。这样的事情对我有用。
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
为每个对象检索一个特定的克隆。或在其上使用clone方法Object
(但由于该方法受到保护,在更多情况下可能会失败)。
其他张贴者是正确的:您需要迭代列表并复制到新列表中。
但是...如果列表中的对象是不可变的,则无需克隆它们。如果您的对象具有复杂的对象图-它们也将必须不可变。
不变性的另一个好处是它们也是线程安全的。
这是使用通用模板类型的解决方案:
public static <T> List<T> copyList(List<T> source) {
List<T> dest = new ArrayList<T>();
for (T item : source) { dest.add(item); }
return dest;
}
使用commons-lang-2.3.jar该Java库克隆列表的简单方法
如何使用
oldList.........
List<YourObject> newList = new ArrayList<YourObject>();
foreach(YourObject obj : oldList){
newList.add((YourObject)SerializationUtils.clone(obj));
}
希望这一点对您有所帮助。
:D
我刚刚开发了一个能够克隆实体对象和java.util.List对象的库。只需在https://drive.google.com/open?id=0B69Sui5ah93EUTloSktFUkctN0U中下载jar 并使用静态方法cloneListObject(List list)。此方法不仅克隆列表,还克隆所有实体元素。
下面为我工作。
在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);
让我知道这是否不适合您。