我有一个ArrayList
,我想完全复制它.我假设有人花了一些时间使其正确,我尽可能使用实用程序类.很自然地,我最终得到了Collections
一个包含复制方法的类.
假设我有以下内容:
Lista = new ArrayList (); a.add("a"); a.add("b"); a.add("c"); List b = new ArrayList (a.size()); Collections.copy(b,a);
这失败了,因为基本上它认为b
不够大a
.是的,我知道b
它的大小为0,但它现在应该足够大,不应该吗?如果我必须先填补b
,那么Collections.copy()
在我的脑海中就会成为一个完全没用的功能.所以,除了编写一个复制函数(我现在要做的)之外,还有一种正确的方法吗?
b
有一个容量为3,但是大小为0的事实ArrayList
具有某种缓冲能力是一个实现细节-它不是一部分List
的接口,所以Collections.copy(List, List)
不使用它.对于特殊情况来说,这将是丑陋的ArrayList
.
正如MrWiggles所指出的那样,使用带有集合的ArrayList构造函数是提供的示例中的方法.
对于更复杂的场景(可能包括您的真实代码),您可能会发现Google Java Collections库非常有用.
调用
Listb = new ArrayList (a);
创建一个浅的a
内部副本b
.所有元素都将以b
与它们所在的完全相同的顺序存在a
(假设它有一个订单).
同样,打电话
// note: instantiating with a.size() gives `b` enough capacity to hold everything Listb = new ArrayList (a.size()); Collections.copy(b, a);
还会创建一个a
内部的浅表副本b
.如果第一个参数,b
没有足够的容量(不是大小)来包含所有a
的元素,那么它将抛出一个IndexOutOfBoundsException
.期望是工作不需要任何分配Collections.copy
,如果有,则抛出该异常.要求复制的集合被预先分配(b
)是一种优化,但我通常不认为该特征是值得的,因为给定基于构造函数的替代方案所需的检查,如上所示,没有任何奇怪的副作用.
要创建深层副本,List
通过任一机制,都必须具有基础类型的复杂知识.对于String
s,它在Java(以及.NET)中是不可变的,你甚至不需要深层复制.在这种情况下MySpecialObject
,您需要知道如何制作它的深层副本,这不是一般操作.
注意:最初接受的答案是Collections.copy
谷歌的最高结果,如评论中所指出的那样,这是错误的.
做就是了:
List a = new ArrayList(); a.add("a"); a.add("b"); a.add("c"); List b = new ArrayList(a);
ArrayList有一个构造函数,它将接受另一个Collection来复制元素
Stephen Katulka(接受的答案)的回答是错误的(第二部分).它解释说它Collections.copy(b, a);
是一个深层复制,它没有.这两种,new ArrayList(a);
而Collections.copy(b, a);
只能做一个浅拷贝.区别在于,构造函数分配新内存,而copy(...)
不是,这使得它适用于可以重用数组的情况,因为它具有性能优势.
Java标准API试图阻止使用深层副本,因为如果新编码器会定期使用它,这将是不好的,这也可能clone()
是默认情况下不公开的原因之一.
源代码Collections.copy(...)
可以在第552行看到:http:
//www.java2s.com/Open-Source/Java-Document/6.0-JDK-Core/Collections-Jar-Zip-Logging-regex/java/util/ Collections.java.htm
如果需要深层复制,则必须手动迭代项目,对每个对象使用for循环和clone().
复制List的最简单方法是将其传递给新列表的构造函数:
Listb = new ArrayList<>(a);
b
将是一个浅薄的副本 a
看看Collections.copy(List,List)
(我以前从未见过它)的来源似乎是通过索引来处理元素索引.使用List.set(int,E)
因此元素0将覆盖目标列表等中的元素0等.从javadocs我不得不承认不是特别清楚.
Lista = new ArrayList<>(a); a.add("foo"); b.add("bar"); List b = new ArrayList<>(a); // shallow copy 'a' // the following will all hold assert a.get(0) == b.get(0); assert a.get(1) == b.get(1); assert a.equals(b); assert a != b; // 'a' is not the same object as 'b'
List b = new ArrayList(a.size())
没有设置大小.它设置初始容量(在需要调整大小之前它可以容纳多少元素).在这种情况下,一种更简单的复制方法是:
List b = new ArrayList(a);
正如hoijui提到的那样.Stephen Katulka的选定答案中包含有关Collections.copy的评论不正确.作者可能接受了它,因为第一行代码正在执行他想要的副本.对Collections.copy的额外调用只会再次复制.(导致复制发生两次).
这是代码来证明它.
public static void main(String[] args) { Lista = new ArrayList (); a.add("a"); a.add("b"); a.add("c"); List b = new ArrayList (a); System.out.println("There should be no output after this line."); // Note, b is already a shallow copy of a; for (int i = 0; i < a.size(); i++) { if (a.get(i) != b.get(i)) { System.out.println("Oops, this was a deep copy."); // Note this is never called. } } // Now use Collections.copy and note that b is still just a shallow copy of a Collections.copy(b, a); for (int i = 0; i < a.size(); i++) { if (a.get(i) != b.get(i)) { System.out.println("Oops, i was wrong this was a deep copy"); // Note this is never called. } } // Now do a deep copy - requires you to explicitly copy each element for (int i = 0; i < a.size(); i++) { b.set(i, new String(a.get(i))); } // Now see that the elements are different in each for (int i = 0; i < a.size(); i++) { if (a.get(i) == b.get(i)) { System.out.println("oops, i was wrong, a shallow copy was done."); // note this is never called. } } }
这里的大多数答案没有意识到问题,用户想要从第一个列表到第二个列表的元素的COPY,目的地列表元素是新对象而不是引用原始列表的元素.(意味着更改第二个列表的元素不应该更改源列表的相应元素的值.)对于可变对象,我们不能使用ArrayList(Collection)构造函数,因为它将简单地引用原始列表元素而不会复制.复制时,每个对象都需要有一个列表克隆器.
为什么不使用addAll
方法:
List a = new ArrayList(); a.add("1"); a.add("abc"); List b = b.addAll(listA); //b will be 1, abc
即使您在b中有现有项目,或者想要在其后面放置一些元素,例如:
List a = new ArrayList(); a.add("1"); a.add("abc"); List b = new ArrayList(); b.add("x"); b.addAll(listA); b.add("Y"); //b will be x, 1, abc, Y