如何克隆数组列表,也克隆它的内容?

我如何克隆一个ArrayList,也克隆它的项目在Java?

例如,我有:

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = ....something to do with dogs....

我希望clonedList中的对象与dogs list中的对象不相同。

479371 次浏览

您将需要迭代这些项,并逐个克隆它们,将克隆放入结果数组中。

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()方法。

你需要手动克隆ArrayList(通过迭代它并将每个元素复制到一个新的ArrayList),因为clone()不会为你做这件事。原因是ArrayList中包含的对象本身不能实现Clonable

编辑:…而这正是Varkhan的代码所做的。

我个人会给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接口并使用它。这样就可以为克隆编写一个泛型方法。

其他的海报是正确的:你需要迭代列表并复制到一个新的列表。

然而< p >… 如果列表中的对象是不可变的-你不需要克隆它们。如果你的对象有一个复杂的对象图,它们也需要是不可变的。< / p >

不可变性的另一个好处是它们也是线程安全的。

我认为目前的绿色答案很糟糕,为什么你会问?

  • 它可能需要添加大量代码
  • 它要求你列出所有要复制的列表并这样做

序列化的方式在我看来也是不好的,你可能不得不到处添加Serializable。

那么解决方案是什么呢?

Java深度克隆库 克隆库是一个小型的开源(apache许可)java库,用于深度克隆对象。对象不必实现克隆接口。实际上,这个库可以克隆任何java对象。它可以用在缓存实现中,如果你不想修改缓存对象,或者当你想创建对象的深度副本时。

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

https://github.com/kostaskougios/cloning看看吧

下面是一个使用泛型模板类型的解决方案:

public static <T> List<T> copyList(List<T> source) {
List<T> dest = new ArrayList<T>();
for (T item : source) { dest.add(item); }
return dest;
}

所有标准集合都有复制构造函数。使用它们。

List<Double> original = // some list
List<Double> copy = new ArrayList<Double>(original); //This does a shallow copy

clone()的设计有几个错误(参见这个问题),所以最好避免使用它。

From Effective Java第二版, Item 11: 明智地覆盖克隆

考虑到所有与Cloneable相关的问题,可以肯定地说 其他接口不应该扩展它,以及类 为继承而设计的(第17项)不应该实现它。因为 它的许多缺点,一些专业的程序员干脆选择永远不做 重写克隆方法,并且永远不要调用它,除非调用 复制数组。如果为继承设计类,请注意If 你选择不提供一个行为良好的保护克隆方法,它 子类将不可能实现克隆。

这本书还描述了复制构造函数相对于克隆/克隆的许多优点。

    他们不依赖于有风险的语言外对象创建 李机制< / >
  • 他们不要求严格遵守文件记录不全的约定
  • 它们与final字段的正确使用并不冲突
  • 它们不会抛出不必要的受控异常
  • 它们不需要类型转换。

考虑使用复制构造函数的另一个好处:假设你有一个HashSet s,你想将它复制为TreeSet。克隆方法不能提供此功能,但使用转换构造函数new TreeSet(s)很容易实现。

对于你的对象重写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....

简单的方法,使用common -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);

基本上有三种不需要手动迭代的方法,

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使用带有int参数的addAll(int index, Collection<? extends E> c)方法

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>();
clonedList.addAll(0, dogs);

注意:如果指定的集合在操作进行时被修改,这些操作的行为将是未定义的。

一种糟糕的方法是用反思来做这件事。这种方法对我很管用。

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<>();
}
}

Java 8提供了一种优雅而简洁地调用元素dogs上的复制构造函数或克隆方法的新方法:Streamslambdas and collectors

拷贝构造函数:

List<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toList());

表达式Dog::new被称为< em >方法引用< / em >。它创建了一个函数对象,调用Dog上的构造函数,该构造函数以另一个dog作为参数。

克隆方法[1]:

List<Dog> clonedDogs = dogs.stream().map(Dog::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.*;


ArrayLists的收集器

上一个例子中的收集器可以被做成一个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 不得clone方法声明它抛出CloneNotSupportedException。原因是map的参数不允许抛出任何受控异常。

是这样的:

    // Note: Method is public and returns Dog, not Object
@Override
public Dog clone() /* Note: No throws clause here */ { ...

然而,这应该不是一个大问题,因为这是最佳实践。(例如Effectice Java给出了这个建议。)

感谢Gustavo注意到这一点

您可以使用JSON(带有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());

你也可以使用其他JSON库,比如Jackson。

使用这种方法的优点是可以解决问题,而不必创建类、接口和克隆逻辑(如果对象内部有其他对象列表,克隆逻辑可能会很长)

我想我找到了一个非常简单的方法来创建一个深度复制数组列表。假设你想复制一个字符串数组列表arrayA。

ArrayList<String>arrayB = new ArrayList<String>();
arrayB.addAll(arrayA);

如果对你不起作用请告诉我。

我刚刚开发了一个库,能够克隆一个实体对象和java.util.List对象。只需在https://drive.google.com/open?id=0B69Sui5ah93EUTloSktFUkctN0U中下载jar,并使用静态方法cloneListObject(List List)。该方法不仅克隆List,而且克隆所有实体元素。

我一直使用这个选项:

ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone);

下面的方法对我有用。

在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;
}
这里通过SerializationUtils.clone()创建了从createCopy方法创建的新列表。 因此对新列表的任何更改都不会影响原始列表

List<Dog> dogs;
List<Dog> copiedDogs = dogs.stream().map(dog -> SerializationUtils.clone(dog)).Collectors.toList());

这将深度复制每个狗

简单的方法是

ArrayList<Dog> dogs = getDogs();
ArrayList<Dog> clonedList = new ArrayList<Dog>(dogs);

一些其他用于将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(狗)“;在上下文:< / p >

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的深度复制成功!< / p >

Alernative 2 -不使用外部包:

一个新的方法&;clone()&;是在Dog类中引入的,并且“实现了serializable”;相对于替代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 +
'}';
}
}

阅读下面main方法中的注释以理解结果。这表明我们已经成功地对ArrayList()进行了深度复制。看到 下面的“克隆()“;上下文中的方法:

.使用实例
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的深度复制成功!< / p >

< >强注一: 方案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