Java集合副本列表 – 我不明白

我有一个ArrayList ,我想完全复制它。 我假设某人花了一些时间来正确使用实用类, 所以很自然地,我最终得到了包含复制方法的Collections类。

假设我有以下几点:

 List<String> a = new ArrayList<String>(); a.add("a"); a.add("b"); a.add("c"); List<String> b = new ArrayList<String>(a.size()); Collections.copy(b,a); 

这个失败,因为它基本上认为b不够大,不能持有。 是的,我知道b大小是0,但是现在应该足够大了,不是吗? 如果我必须先填充b ,那么Collections.copy()在我脑海中变成一个完全无用的函数。 所以,除了编程一个复制function(我现在要做的)是否有一个适当的方法来做到这一点?

调用

 List<String> b = new ArrayList<String>(a); 

b创build一个浅的副本。 所有的元素将以b中的顺序存在于b中(假设它有一个顺序)。

同样,打电话

 // note: instantiating with a.size() gives `b` enough capacity to hold everything List<String> b = new ArrayList<String>(a.size()); Collections.copy(b, a); 

也创build一个b内的浅拷贝。 如果第一个参数b没有足够的容量 (不是大小)来包含所有的元素,那么它将抛出一个IndexOutOfBoundsException 。 期望是Collections.copy不需要任何分配来工作,如果有的话,它会抛出exception。 这是一个优化,要求复制的集合被预先分配( b ),但我通常不认为该function是值得的,由于所需的检查给予基于构造函数的替代品,如上所示,没有奇怪的副作用。

要创build深层副本,通过任何一种机制, List都必须具有复杂的底层types知识。 对于在Java中不可变的String (和.NET),您甚至不需要深度复制。 在MySpecialObject的情况下,你需要知道如何做一个深层次的副本,这不是一个通用的操作。


注意:最初接受的答案是Google的Collections.copy的最高结果,正如评论中指出的那样,答案是错误的。

b容量为3,但大小ArrayList具有某种缓冲区容量的事实是一个实现细节 – 它不是List接口的一部分,所以Collections.copy(List, List)不使用它。 这对于特殊的ArrayList来说会很难看。

正如MrWiggles所指出的那样,使用ArrayList构造函数接收一个集合是在示例中提供的方式。

对于更复杂的场景(可能包含真实的代码),您可能会发现Google Java Collections库很有用。

做就是了:

 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正则expression式/ JAVA / UTIL / Collections.java.htm

如果您需要深度复制,则必须手动迭代这些项目,在每个对象上使用for循环和clone()。

复制列表最简单的方法是将其传递给新列表的构造函数:

 List<String> b = new ArrayList<>(a); 

b将是一个浅的副本

看着Collections.copy(List,List)的源头(我以前从来没有见过),它似乎是通过索引来处理元素索引。 使用List.set(int,E)因此元素0将在目标列表中写入元素0等等。从javadocs中我不得不承认。

 List<String> a = new ArrayList<>(a); a.add("foo"); b.add("bar"); List<String> 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()) 

不设置大小。 它设置了初始容量(在需要resize之前可以容纳多less元素)。 在这种情况下,更简单的复制方法是:

 List b = new ArrayList(a); 

正如hoijui提到的。 Stephen Katulka所选的答案中包含关于Collections.copy的评论,这是不正确的。 作者可能会接受它,因为第一行代码是在做他想要的副本。 对Collections.copy的额外调用只是再次复制。 (导致复制发生两次)。

这里是代码来certificate它。

 public static void main(String[] args) { List<String> a = new ArrayList<String>(); a.add("a"); a.add("b"); a.add("c"); List<String> b = new ArrayList<String>(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 

如果要复制ArrayList,请使用以下命令复制它:

 List b = new ArrayList(); b.add("aa"); b.add("bb"); List a = new ArrayList(b); 

string可以被深度复制

 List<String> b = new ArrayList<String>(a); 

因为它们是不可改变的。 其他的对象不是 – >你需要迭代并自己做一个副本。

 private List<Item> cloneItemList(final List<Item> items) { Item[] itemArray = new Item[items.size()]; itemArray = items.toArray(itemArray); return Arrays.asList(itemArray); } 

其他的对象不是 – >你需要迭代并自己做一个副本。

为了避免这个实现Cloneable。

 public class User implements Serializable, Cloneable { private static final long serialVersionUID = 1L; private String user; private String password; ... @Override public Object clone() { Object o = null; try { o = super.clone(); } catch(CloneNotSupportedException e) { } return o; } } 

….

  public static void main(String[] args) { List<User> userList1 = new ArrayList<User>(); User user1 = new User(); user1.setUser("User1"); user1.setPassword("pass1"); ... User user2 = new User(); user2.setUser("User2"); user2.setPassword("pass2"); ... userList1 .add(user1); userList1 .add(user2); List<User> userList2 = new ArrayList<User>(); for(User u: userList1){ u.add((User)u.clone()); } //With this you can avoid /* for(User u: userList1){ User tmp = new User(); tmp.setUser(u.getUser); tmp.setPassword(u.getPassword); ... u.add(tmp); } */ } 

以下输出说明了使用复制构造函数和Collections.copy()的结果:

 Copy [1, 2, 3] to [1, 2, 3] using copy constructor. Copy [1, 2, 3] to (smaller) [4, 5] java.lang.IndexOutOfBoundsException: Source does not fit in dest at java.util.Collections.copy(Collections.java:556) at com.farenda.java.CollectionsCopy.copySourceToSmallerDest(CollectionsCopy.java:36) at com.farenda.java.CollectionsCopy.main(CollectionsCopy.java:14) Copy [1, 2] to (same size) [3, 4] source: [1, 2] destination: [1, 2] Copy [1, 2] to (bigger) [3, 4, 5] source: [1, 2] destination: [1, 2, 5] Copy [1, 2] to (unmodifiable) [4, 5] java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableList.set(Collections.java:1311) at java.util.Collections.copy(Collections.java:561) at com.farenda.java.CollectionsCopy.copyToUnmodifiableDest(CollectionsCopy.java:68) at com.farenda.java.CollectionsCopy.main(CollectionsCopy.java:20) 

完整程序的来源在这里: Java列表副本 。 但是输出足以查看java.util.Collections.copy()的行为。

如果你使用谷歌番石榴,一线解决scheme将是

 List<String> b = Lists.newArrayList(a); 

这将创build一个可变数组列表实例。

如果您想象用例将某些值复制到现有集合中,则复制不是无用的。 即你想覆盖现有的元素,而不是插入。

例如:a = [1,2,3,4,5] b = [2,2,2,2,3,3,3,3,3,4,4,4,] a.copy(b) = [1,2,3,4,5,3,3,3,3,4,4,4]

不过,我期望一个复制方法,将采取额外的参数为源和目标集合的开始索引,以及为count的参数。

请参阅Java BUG 6350752

为了理解为什么Collections.copy()抛出一个IndexOutOfBoundsException,虽然你已经使目标列表的支持数组足够大(通过sourceList上的size()调用),请参阅Abhay Yadav在这个相关问题中的答案: 将java.util.List复制到另一个java.util.List