如何克隆ArrayList
并在Java中克隆其项?
例如我有:
ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....
我希望clonedList
中的对象与狗列表中的对象不同。
答案 0 :(得分:186)
我个人会向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中使用破解的Cloneable东西。它还与您复制Java集合的方式相匹配。
另一种选择可能是编写自己的ICloneable接口并使用它。这样你就可以编写一个通用的克隆方法。
答案 1 :(得分:184)
您将需要迭代这些项目,并逐个克隆它们,随时将克隆放入结果数组中。
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()
方法。
答案 2 :(得分:133)
所有标准集合都有复制构造函数。使用它们。
List<Double> original = // some list
List<Double> copy = new ArrayList<Double>(original); //This does a shallow copy
clone()
设计有多个错误(请参阅this question),因此最好避免错误。
来自Effective Java 2nd Edition,第11项:明智地覆盖克隆
考虑到与Cloneable相关的所有问题,可以说是安全的 其他接口不应该扩展它,以及那些类 为继承而设计(第17项)不应该实现它。因为 它的许多缺点,一些专家程序员只是选择永远不会 覆盖克隆方法,永远不要调用它,除非可能 复制数组。如果您设计了一个继承类,请注意if 你选择不提供一个行为良好的受保护克隆方法 子类不可能实现Cloneable。
本书还介绍了复制构造函数相对于Cloneable / clone的许多优点。
考虑使用复制构造函数的另一个好处:假设您有HashSet s
,并且希望将其复制为TreeSet
。克隆方法无法提供此功能,但使用转换构造函数很容易:new TreeSet(s)
。
答案 3 :(得分:36)
Java 8提供了一种新方法,可以优雅紧凑地在元素狗上调用复制构造函数或克隆方法:Streams, lambdas and collectors。
复制构造函数:
List<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toList());
表达式Dog::new
称为method reference。它创建了一个函数对象,它调用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 上一个示例中的收集器可以构成一个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
。原因是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());
答案 4 :(得分:28)
基本上有三种方法没有手动迭代,
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);
注意:如果在操作过程中修改了指定的集合,则这些操作的行为将是不确定的。
答案 5 :(得分:17)
我认为目前的绿色答案很糟糕,为什么你会问?
序列化的方式也很糟糕,你可能需要在整个地方添加Serializable。
那么解决方案是什么:
Java Deep-Cloning库 克隆库是一个小型的开源(apache许可证)java库,它深入克隆对象。对象不必实现Cloneable接口。实际上,这个库可以克隆任何java对象。如果您不希望修改缓存对象或者只是想要创建对象的深层副本,则可以在缓存实现中使用它。
Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);
上查看
答案 6 :(得分:3)
我找到了一种方法,你可以使用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库来做到这一点。
答案 7 :(得分:2)
您需要手动克隆ArrayList
(通过迭代并将每个元素复制到新的ArrayList
),因为clone()
不会为您执行此操作。原因是ArrayList
中包含的对象可能无法自己实现Clonable
。
编辑:......这正是Varkhan的代码所做的。
答案 8 :(得分:1)
令人讨厌的方法是用反射来做。这样的事情对我有用。
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<>();
}
}
答案 9 :(得分:1)
将ArrayList复制为深拷贝的其他替代方法
替代项1 -使用外部包commons-lang3,方法 SerializationUtils.clone():
SerializationUtils.clone()
比方说,我们有一个class dog,其中该类的字段是可变的,并且至少一个字段是String和mutable类型的对象-而不是原始数据类型(否则,浅表复制就足够了。)
浅拷贝示例:
List<Dog> dogs = getDogs(); // We assume it returns a list of Dogs
List<Dog> clonedDogs = new ArrayList<>(dogs);
现在回到狗的深层副本。
Dog类仅具有可变字段。
狗类:
public class Dog implements Serializable {
private String name;
private int age;
public Dog() {
// Class with only mutable fields!
this.name = "NO_NAME";
this.age = -1;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
请注意,Dog类实现了Serializable!这样就可以利用方法“ SerializationUtils.clone(dog)”
阅读main方法中的注释以了解结果。它表明我们已经成功制作了ArrayList()的深层副本。看到 上下文中“ SerializationUtils.clone(dog)”下方的
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.setName("Buddy");
dog1.setAge(1);
Dog dog2 = new Dog();
dog2.setName("Milo");
dog2.setAge(2);
List<Dog> dogs = new ArrayList<>(Arrays.asList(dog1,dog2));
// Output: 'List dogs: [Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]'
System.out.println("List dogs: " + dogs);
// Let's clone and make a deep copy of the dogs' ArrayList with external package commons-lang3:
List<Dog> clonedDogs = dogs.stream().map(dog -> SerializationUtils.clone(dog)).collect(Collectors.toList());
// Output: 'Now list dogs are deep copied into list clonedDogs.'
System.out.println("Now list dogs are deep copied into list clonedDogs.");
// A change on dog1 or dog2 can not impact a deep copy.
// Let's make a change on dog1 and dog2, and test this
// statement.
dog1.setName("Bella");
dog1.setAge(3);
dog2.setName("Molly");
dog2.setAge(4);
// The change is made on list dogs!
// Output: 'List dogs after change: [Dog{name='Bella', age=3}, Dog{name='Molly', age=4}]'
System.out.println("List dogs after change: " + dogs);
// There is no impact on list clonedDogs's inner objects after the deep copy.
// The deep copy of list clonedDogs was successful!
// If clonedDogs would be a shallow copy we would see the change on the field
// "private String name", the change made in list dogs, when setting the names
// Bella and Molly.
// Output clonedDogs:
// 'After change in list dogs, no impact/change in list clonedDogs:\n'
// '[Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]\n'
System.out.println("After change in list dogs, no impact/change in list clonedDogs: \n" + clonedDogs);
}
输出:
List dogs: [Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]
Now list dogs are deep copied into list clonedDogs.
List dogs after change: [Dog{name='Bella', age=3}, Dog{name='Molly', age=4}]
After change in list dogs, no impact/change in list clonedDogs:
[Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]
评论: 由于更改列表狗后对列表clonedDogs没有影响/更改, 则ArrayList的深层复制成功!
替代2 -不使用外部软件包:
在Dog类中引入了新方法“ clone()”,与替代方法1相比,“实现可序列化的实现”已删除。
clone()
狗类:
public class Dog {
private String name;
private int age;
public Dog() {
// Class with only mutable fields!
this.name = "NO_NAME";
this.age = -1;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* Returns a deep copy of the Dog
* @return new instance of {@link Dog}
*/
public Dog clone() {
Dog newDog = new Dog();
newDog.setName(this.name);
newDog.setAge(this.age);
return newDog;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
阅读下面主要方法中的注释以了解结果。它表明我们已经成功制作了ArrayList()的深层副本。看到 在上下文中的“ clone()”方法下面:
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.setName("Buddy");
dog1.setAge(1);
Dog dog2 = new Dog();
dog2.setName("Milo");
dog2.setAge(2);
List<Dog> dogs = new ArrayList<>(Arrays.asList(dog1,dog2));
// Output: 'List dogs: [Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]'
System.out.println("List dogs: " + dogs);
// Let's clone and make a deep copy of the dogs' ArrayList:
List<Dog> clonedDogs = dogs.stream().map(dog -> dog.clone()).collect(Collectors.toList());
// Output: 'Now list dogs are deep copied into list clonedDogs.'
System.out.println("Now list dogs are deep copied into list clonedDogs.");
// A change on dog1 or dog2 can not impact a deep copy.
// Let's make a change on dog1 and dog2, and test this
// statement.
dog1.setName("Bella");
dog1.setAge(3);
dog2.setName("Molly");
dog2.setAge(4);
// The change is made on list dogs!
// Output: 'List dogs after change: [Dog{name='Bella', age=3}, Dog{name='Molly', age=4}]'
System.out.println("List dogs after change: " + dogs);
// There is no impact on list clonedDogs's inner objects after the deep copy.
// The deep copy of list clonedDogs was successful!
// If clonedDogs would be a shallow copy we would see the change on the field
// "private String name", the change made in list dogs, when setting the names
// Bella and Molly.
// Output clonedDogs:
// 'After change in list dogs, no impact/change in list clonedDogs:\n'
// '[Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]\n'
System.out.println("After change in list dogs, no impact/change in list clonedDogs: \n" + clonedDogs);
}
输出:
List dogs: [Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]
Now list dogs are deep copied into list clonedDogs.
List dogs after change: [Dog{name='Bella', age=3}, Dog{name='Molly', age=4}]
After change in list dogs, no impact/change in list clonedDogs:
[Dog{name='Buddy', age=1}, Dog{name='Milo', age=2}]
评论: 由于更改列表狗后对列表clonedDogs没有影响/更改, 则ArrayList的深层复制成功!
注意1: 备选方案1比备选方案2慢得多 但由于不需要,维护起来更容易 更新诸如clone()之类的任何方法。
注意2:对于替代方法1,以下maven依赖项用于方法“ SerializationUtils.clone()”:
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
在以下位置找到更多common-lang3版本:
https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
答案 10 :(得分:0)
使用commons-lang-2.3.jar简单的方法,即java库来克隆列表
链接download commons-lang-2.3.jar
如何使用
oldList.........
List<YourObject> newList = new ArrayList<YourObject>();
foreach(YourObject obj : oldList){
newList.add((YourObject)SerializationUtils.clone(obj));
}
我希望这个有用。
:d
答案 11 :(得分:0)
包import org.apache.commons.lang.SerializationUtils;
有一种方法SerializationUtils.clone(Object);
实施例
this.myObjectCloned = SerializationUtils.clone(this.object);
答案 12 :(得分:0)
for you objects override clone()method
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;
}
}
并为Vector obj或ArraiList obj ....
调用.clone()答案 13 :(得分:0)
以下是使用通用模板类型的解决方案:
public static <T> List<T> copyList(List<T> source) {
List<T> dest = new ArrayList<T>();
for (T item : source) { dest.add(item); }
return dest;
}
答案 14 :(得分:0)
其他海报是正确的:您需要迭代列表并复制到新列表中。
...然而 如果列表中的对象是不可变的 - 您不需要克隆它们。如果您的对象具有复杂的对象图 - 它们也需要是不可变的。
不变性的另一个好处是它们也是线程安全的。
答案 15 :(得分:0)
我刚刚开发了一个能够克隆实体对象和java.util.List对象的lib。只需在https://drive.google.com/open?id=0B69Sui5ah93EUTloSktFUkctN0U中下载jar并使用静态方法cloneListObject(List list)。此方法不仅克隆List,还克隆所有实体元素。
答案 16 :(得分:0)
我总是使用这个选项:
ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone);
答案 17 :(得分:0)
以下内容对我有用。
在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()创建的。 因此,对新列表所做的任何更改都不会影响原始列表
答案 18 :(得分:0)
TypeError: 'PySide2.QtWidgets.QBoxLayout.addWidget' called with wrong argument types:
PySide2.QtWidgets.QBoxLayout.addWidget(GLViewWidget)
Supported signatures:
PySide2.QtWidgets.QBoxLayout.addWidget(PySide2.QtWidgets.QWidget, int = 0, PySide2.QtCore.Qt.Alignment = 0)
这将深深复制每只狗
答案 19 :(得分:0)
简单的方法是
ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>(dogs);
答案 20 :(得分:-1)
我认为我找到了一种非常简单的方法来制作深度复制ArrayList。假设您要复制String ArrayList arrayA。
ArrayList<String>arrayB = new ArrayList<String>();
arrayB.addAll(arrayA);
如果它不适合你,请告诉我。