How to clone ArrayList and also clone its contents?
如何克隆
例如,我有:
1 2 | ArrayList<Dog> dogs = getDogs(); ArrayList<Dog> clonedList = ....something to do with dogs.... |
而且我希望
您将需要迭代这些项目,并逐个克隆它们,然后将克隆放入结果数组中。
1 2 3 4 5 | 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添加一个构造函数:
1 2 3 4 5 6 7 8 9 | class Dog { public Dog() { ... } // Regular constructor public Dog(Dog dog) { // Copy all the fields of Dog. } } |
然后进行迭代(如Varkhan的答案所示):
1 2 3 4 5 6 7 | 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接口并使用它。这样,您可以编写通用的克隆方法。
所有标准集合都有副本构造函数。使用它们。
1 2 | List<Double> original = // some list List<Double> copy = new ArrayList<Double>(original); //This does a shallow copy |
摘自Effective Java 2nd Edition,第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.
本书还描述了复制构造函数相对于Cloneable / clone的许多优点。
-
他们不依赖于容易产生风险的语言外对象的创建
机制 - 他们不需要强制执行严格记录在案的约定
- 它们与正确使用最终字段不冲突
- 他们不会抛出不必要的检查异常
- 他们不需要演员。
考虑使用复制构造函数的另一个好处:假设您有一个
Java 8提供了一种新的方式来优雅而紧凑地在元素狗上调用复制构造函数或克隆方法:流,lambda和收集器。
复制构造函数:
1 | List<Dog> clonedDogs = dogs.stream().map(Dog::new).collect(toList()); |
表达式
克隆方法[1]:
1 | List<Dog> clonedDogs = dogs.stream().map(d -> d.clone()).collect(toList()); |
结果为
或者,如果您必须找回
1 |
到位更新列表
如果不需要保留
1 | dogs.replaceAll(Dog::new); |
所有示例均假定
上一个示例中的收集器可以做成util方法。由于这是一件很常见的事情,因此我个人希望它短而漂亮。像这样:
1 2 3 4 5 | 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]关于
为了使该解决方案有效,必须不要声明
像这样:
1 2 3 | // Note: Method is public and returns Dog, not Object @Override public Dog clone() /* Note: No throws clause here */ { ... |
但是,这应该不是一个大问题,因为无论如何这都是最佳实践。 (例如,Effectice Java提供了此建议。)
感谢Gustavo注意到这一点。
PS:
如果发现它更漂亮,则可以使用方法引用语法来做完全相同的事情:
1 | List<Dog> clonedDogs = dogs.stream().map(Dog::clone).collect(toList()); |
基本上,有三种方法无需手动进行迭代,
1使用构造函数
1 2 | ArrayList<Dog> dogs = getDogs(); ArrayList<Dog> clonedList = new ArrayList<Dog>(dogs); |
2使用
1 2 3 | ArrayList<Dog> dogs = getDogs(); ArrayList<Dog> clonedList = new ArrayList<Dog>(); clonedList.addAll(dogs); |
3将
1 2 3 | ArrayList<Dog> dogs = getDogs(); ArrayList<Dog> clonedList = new ArrayList<Dog>(); clonedList.addAll(0, dogs); |
注意:如果在操作进行过程中修改了指定的集合,则这些操作的行为将不
我认为当前的绿色答案很不好,为什么您会问?
- 可能需要添加很多代码
- 它要求您列出所有要复制的列表并执行此操作
imo序列化的方式也很糟糕,您可能不得不在各处添加Serializable。
那么解决方案是什么:
Java深度克隆库
克隆库是一个小型的,开放源代码(Apache许可证)的Java库,用于深度克隆对象。对象不必实现Cloneable接口。实际上,该库可以克隆任何java对象。如果您不希望修改缓存的对象,或者想要创建对象的深层副本,则可以在缓存实现中使用它。
1 2 | Cloner cloner=new Cloner(); XX clone = cloner.deepClone(someObjectOfTypeXX); |
在https://github.com/kostaskougios/cloning中查看
我找到了一种方法,您可以使用json对列表进行序列化/反序列化。未序列化时,序列化列表不包含对原始对象的引用。
使用gson:
1 2 3 | 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库来实现。
我一直使用此选项:
1 | ArrayList<Dog> clonedList = new ArrayList<Dog>(name_of_arraylist_that_you_need_to_Clone); |
您将需要手动克隆
编辑:...这正是Varkhan的代码所做的。
讨厌的方法是反射。这样的事情对我有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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<>(); } } |
包
有一种方法
例
1 | this.myObjectCloned = SerializationUtils.clone(this.object); |
其他张贴者是正确的:您需要迭代列表并复制到新列表中。
然而...
如果列表中的对象是不可变的,则无需克隆它们。如果您的对象具有复杂的对象图-它们也将必须不可变。
不变性的另一个好处是它们也是线程安全的。
这是使用通用模板类型的解决方案:
1 2 3 4 5 | 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库克隆列表的简单方法
链接下载commons-lang-2.3.jar
如何使用
1 2 3 4 5 | 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)。此方法不仅克隆列表,还克隆所有实体元素。
为您的对象覆盖clone()方法
1 2 3 4 5 6 7 8 9 10 11 | 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()。
我认为我找到了制作深拷贝ArrayList的一种非常简单的方法。假设您要复制一个String ArrayList arrayA。
1 2 | ArrayList<String>arrayB = new ArrayList<String>(); arrayB.addAll(arrayA); |
让我知道这是否对您不起作用。