我如何克隆一个数组列表,也克隆其项目在Java?
例如,我有:
ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....
我希望clonedList中的对象与dogs list中的对象不一样。
我如何克隆一个数组列表,也克隆其项目在Java?
例如,我有:
ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....
我希望clonedList中的对象与dogs list中的对象不一样。
当前回答
下面是一个使用泛型模板类型的解决方案:
public static <T> List<T> copyList(List<T> source) {
List<T> dest = new ArrayList<T>();
for (T item : source) { dest.add(item); }
return dest;
}
其他回答
一些其他用于将ArrayList复制为深度复制的替代方法
Alernative 1 -使用外部包common -lang3,方法SerializationUtils.clone():
SerializationUtils.clone()
假设我们有一个类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}]
备注: 因为改变列表狗后对列表克隆狗没有影响/改变, 那么ArrayList的深度复制成功!
Alernative 2 -不使用外部包:
Dog类中引入了一个新方法“clone()”,与替代方案1相比,“implements Serializable”被删除了。
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}]
备注: 因为改变列表狗后对列表克隆狗没有影响/改变, 那么ArrayList的深度复制成功!
注一: 方案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
对于你的对象重写clone()方法
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() for Vector obj或ArraiList obj....
Java 8提供了一种优雅而简洁地调用元素狗的复制构造函数或克隆方法的新方法:流、lambdas和收集器。
拷贝构造函数:
List<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toList());
表达式Dog::new被称为方法引用。它创建了一个函数对象,调用Dog上的构造函数,该构造函数以另一个Dog作为参数。
克隆方法[1]:
List<Dog> clonedDogs = dogs.stream().map(Dog::clone).collect(toList());
得到一个数组列表作为结果
或者,如果你必须得到一个数组列表(以防你以后想修改它):
ArrayList<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toCollection(ArrayList::new));
及时更新列表
如果你不需要保留狗列表的原始内容,你可以使用replaceAll方法并在适当的地方更新列表:
dogs.replaceAll(Dog::new);
所有示例都假设导入静态java.util.stream.Collectors.*;
数组列表收集器
上一个例子中的收集器可以被做成一个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);
}
[1] CloneNotSupportedException异常说明:
为了使这个解决方案工作,Dog的克隆方法不能声明它抛出CloneNotSupportedException。原因是map的参数不允许抛出任何受控异常。
是这样的:
// Note: Method is public and returns Dog, not Object
@Override
public Dog clone() /* Note: No throws clause here */ { ...
然而,这应该不是一个大问题,因为这是最佳实践。(例如,effective Java给出了这个建议。)
感谢Gustavo注意到这一点。
所有标准集合都有复制构造函数。使用它们。
List<Double> original = // some list
List<Double> copy = new ArrayList<Double>(original); //This does a shallow copy
Clone()的设计有几个错误(请参阅这个问题),所以最好避免使用它。
来自Effective Java第二版,第11项:明智地覆盖克隆
Given all of the problems associated with Cloneable, it’s safe to say that other interfaces should not extend it, and that classes designed for inheritance (Item 17) should not implement it. Because of its many shortcomings, some expert programmers simply choose never to override the clone method and never to invoke it except, perhaps, to copy arrays. If you design a class for inheritance, be aware that if you choose not to provide a well-behaved protected clone method, it will be impossible for subclasses to implement Cloneable.
这本书还描述了复制构造函数相对于克隆/克隆的许多优点。
他们不依赖于有风险的语言外对象创造 机制 他们不要求严格遵守文件记录不全的约定 它们与final字段的正确使用并不冲突 它们不会抛出不必要的受控异常 它们不需要类型转换。
考虑使用复制构造函数的另一个好处:假设您有一个HashSet,并且希望将其复制为TreeSet。克隆方法不能提供这种功能,但是使用转换构造函数new TreeSet(s)就很容易实现。
其他的海报是正确的:你需要迭代列表并复制到一个新的列表。
然而…… 如果列表中的对象是不可变的-你不需要克隆它们。如果你的对象有一个复杂的对象图,它们也需要是不可变的。
不可变性的另一个好处是它们也是线程安全的。