Java 收藏。复制问题

Java 收藏。复制问题,java,collections,Java,Collections,我希望b1和b2有自己的元素集,那么b1和b2应该在内存中有自己的元素,这样当b1/b2被修改时,其他元素就不会受到影响 buffer是一个包含许多元素的ArrayList List<Integer> b1 = new ArrayList<Integer>(buffer.size()) ; List<Integer> b2 = new ArrayList<Integer>(buffer.size()) ) ; Collections.copy(

我希望
b1
b2
有自己的元素集,那么b1和b2应该在内存中有自己的元素,这样当b1/b2被修改时,其他元素就不会受到影响

buffer
是一个包含许多元素的
ArrayList

List<Integer>  b1 = new ArrayList<Integer>(buffer.size()) ;
List<Integer>  b2 = new ArrayList<Integer>(buffer.size()) ) ;
Collections.copy(b1, buffer);
Collections.copy(b2, buffer);
List b1=新的ArrayList(buffer.size());
列表b2=新的ArrayList(buffer.size());
集合。副本(b1,缓冲区);
集合。副本(b2,缓冲区);
我得到一个例外:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Source does not fit in dest at java.util.Collections.copy(Collections.java:531) at Trees.containsSumPrint(Trees.java:243) at Trees.main(Trees.java:125) 线程“main”中出现异常 java.lang.IndexOutOfBoundsException:源不适合于dest 位于java.util.Collections.copy(Collections.java:531) at Trees.containsSumPrint(Trees.java:243) at Trees.main(Trees.java:125)
ArrayList(int)
构造函数提供一个大小为0的
List
,它只确保在需要重新分配基础数组之前可以添加
n
元素

复制列表的更好方法是:

b1.addAll(buffer);
b2.addAll(buffer);
语义与第一次向每个数组添加
buffer.size()
null并调用
Collections.copy(b1,buffer)时相同


如果需要深度复制(元素也被复制),则必须分别处理每个元素

for(MyObject obj:buffer){
    b1.add(obj.clone());
    b2.add(obj.clone());
}
Collections.copy(…)
说明:

“将所有元素从一个列表复制到另一个列表中。操作完成后,目标列表中每个复制元素的索引将与其在源列表中的索引相同目标列表必须至少与源列表一样长。如果更长,则目标列表中的其余元素不受影响。“

ArrayList(int)
构造函数创建一个空列表,其容量(不是大小!)由参数给定

由于
b1
最初为空,因此将非空列表复制到其中(使用
copy
)将失败,因为前提条件(粗体)通常不成立

基本上,
Collections.copy(…)
是错误的使用方法

你真正应该做的是:

List<Integer> b1 = new ArrayList<Integer>(buffer.size());
List<Integer> b2 = new ArrayList<Integer>(buffer.size());
b1.addAll(buffer);
b2.addAll(buffer);
List b1=新的ArrayList(buffer.size());
列表b2=新的ArrayList(buffer.size());
b1.addAll(缓冲区);
b2.addAll(缓冲区);


我假设您真的不想创建列表元素的新实例。如果您想创建,我应该指出创建
Integer
对象的新实例是浪费时间的,因为
Integer
(与其他包装类和
String
)是一个不可变的类。

您需要每个元素的深度副本。没有标准的方法来实现这一点,因为深度复制可能涉及复制对其他对象(集合)的嵌套引用。最好的方法是创建一个副本构造函数,java.lang.Integer恰好有一个!因此我认为您应该执行以下操作:

List<Integer> buffer = Arrays.asList(new Integer[] { 0, 1, 2, 3, 4 });
List<Integer> b1 = new ArrayList<Integer>();
List<Integer> b2 = new ArrayList<Integer>();

for (Integer element : buffer) {
    b1.add(new Integer(element));
    b2.add(new Integer(element));
}
请注意,还存在可克隆接口。我建议不要使用此接口,因为它很容易在引用的类、集合和子类中出错。复制构造函数更容易正确。请参阅以获得一些佐证

编辑:重读时,可能您不需要深度副本,在这种情况下,您可以使用其他人描述的“addAll”方法。这将允许您创建相同对象实例的多个集合。然后您可以修改一个集合中对象的内容/顺序,而不影响其他集合。但是,如果您在当然,这显然也会反映在所有其他收藏中


此外,StephenC正确地指出,我上面的例子很疯狂。我同意,通常情况下,人们永远不会“深度复制”那样的整数,但对于包含集合/引用的自定义对象来说,这是有意义的,我认为这是问题所在。

这不会创建两个不同(复制)的列表对象,这是OP想要的。在我们执行b1.addALL(buffer)之后,buffer和b1都会有自己单独的元素副本吗?修改一个会影响另一个吗?每个都会有自己的集合,您可以单独修改。但是调用b1.get(0).doSomething()也会影响b2.get(0)(它们仍然是相同的对象)但问题中的整数是不可变的,所以你可以让它们共享对象。这很疯狂。除非OP做了一些奇怪的事情,否则创建新的
Integer
对象是浪费时间……即使你认为这是他要求的。@StephenC:同意。我假设OP在这里使用整数作为示例,并且确实想要对任意复杂度的对象执行此操作。
for (Integer element : buffer) {
    b1.add(new Integer(element));
    b2.add(element);
}