如何克隆ArrayList
并在Java中克隆其项目?
例如,我有:
ArrayListdogs = getDogs(); ArrayList clonedList = ....something to do with dogs....
我希望那些物品clonedList
与狗列表中的物品不同.
您将需要迭代这些项目,并逐个克隆它们,然后将克隆放入结果数组中.
public static ListcloneList(List list) { List clone = new ArrayList (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 ListcloneList(List dogList) { List clonedList = new ArrayList (dogList.size()); for (Dog dog : dogList) { clonedList.add(new Dog(dog)); } return clonedList; }
我发现这样做的好处是你不需要在Java中使用破解的Cloneable东西.它还与您复制Java集合的方式相匹配.
另一种选择可能是编写自己的ICloneable接口并使用它.这样你就可以编写一个通用的克隆方法.
所有标准集合都有拷贝构造函数.使用它们.
Listoriginal = // some list List copy = new ArrayList (original); //This does a shallow copy
clone()
设计有几个错误(见这个问题),所以最好避免它.
从Effective Java 2nd Edition,第11项:明智地覆盖克隆
鉴于与Cloneable相关的所有问题,可以肯定地说其他接口不应该扩展它,并且为继承而设计的类(第17项)不应该实现它.由于它有许多缺点,一些专家程序员只是选择永远不要覆盖克隆方法,永远不要调用它,除非复制数组.如果您设计了一个继承类,请注意,如果您选择不提供行为良好的受保护克隆方法,则子类将无法实现Cloneable.
本书还介绍了复制构造函数相对于Cloneable/clone的许多优点.
他们不依赖于易于冒险的语言外对象创建机制
他们并不要求无法强制遵守精简文件的惯例
它们与正确使用最终字段不冲突
它们不会抛出不必要的已检查异常
他们不需要演员阵容.
考虑使用复制构造函数的另一个好处:假设您有一个HashSet s
,并且您希望将其复制为TreeSet
.克隆方法无法提供此功能,但转换构造函数很容易:new TreeSet(s)
.
Java 8提供了一种新方法,可以优雅紧凑地调用元素狗上的复制构造函数或克隆方法:Streams,lambdas和collectors.
复制构造函数:
ListclonedDogs = dogs.stream().map(Dog::new).collect(toList());
该表达式Dog::new
称为方法引用.它创建了一个函数对象,该对象调用构造函数,在Dog
该构造函数上将另一个狗作为参
克隆方法[1]:
ListclonedDogs = dogs.stream().map(d -> d.clone()).collect(toList());
ArrayList
结果或者,如果你需要ArrayList
退回(如果你想稍后修改它):
ArrayListclonedDogs = dogs.stream().map(Dog::new).collect(toCollection(ArrayList::new));
如果您不需要保留dogs
列表的原始内容,则可以使用该replaceAll
方法并更新列表:
dogs.replaceAll(Dog::new);
所有例子都假设import static java.util.stream.Collectors.*;
.
ArrayList
s可以将最后一个示例中的收集器制成util方法.因为这是一件很常见的事情,我个人喜欢它简短而漂亮.像这样:
ArrayListclonedDogs = dogs.stream().map(d -> d.clone()).collect(toArrayList()); public static Collector > 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注意到这一点.
如果你发现它更漂亮,你可以使用方法引用语法来完成同样的事情:
ListclonedDogs = dogs.stream().map(Dog::clone).collect(toList());
基本上有三种方法没有手动迭代,
1使用构造函数
ArrayListdogs = getDogs(); ArrayList clonedList = new ArrayList (dogs);
2使用 addAll(Collection extends E> c)
ArrayListdogs = getDogs(); ArrayList clonedList = new ArrayList (); clonedList.addAll(dogs);
3使用addAll(int index, Collection extends E> c)
带int
参数的方法
ArrayListdogs = getDogs(); ArrayList clonedList = new ArrayList (); clonedList.addAll(0, dogs);
注意:如果在操作过程中修改了指定的集合,则将不确定这些操作的行为.
我认为目前的绿色答案很糟糕,为什么你会问?
它可能需要添加大量代码
它要求您列出要复制的所有列表并执行此操作
序列化的方式也很糟糕,你可能需要在整个地方添加Serializable.
那么解决方案是什么:
Java深度 克隆库克隆库是一个小型的开源(apache许可证)java库,它深入克隆对象.对象不必实现Cloneable接口.实际上,这个库可以克隆任何java对象.如果您不希望修改缓存对象或者只是想要创建对象的深层副本,则可以在缓存实现中使用它.
Cloner cloner=new Cloner(); XX clone = cloner.deepClone(someObjectOfTypeXX);
请访问https://github.com/kostaskougios/cloning查看
我找到了一种方法,您可以使用json对列表进行序列化/反序列化。未序列化时,序列化列表不包含对原始对象的引用。
使用gson:
ListoriginalList = new ArrayList<>(); // add some items later String listAsJson = gson.toJson(originalList); List newList = new Gson().fromJson(listAsJson, new TypeToken >() {}.getType());
您也可以使用jackson和其他任何json库来实现。
我一直使用此选项:
ArrayListclonedList = new ArrayList (name_of_arraylist_that_you_need_to_Clone);