如何将一个java.util.List复制到另一个java.util.List中

180

我有一个从Web服务中填充的 List<SomeBean>,我想将该列表的内容复制/克隆到同类型的空列表中。在谷歌搜索复制列表时,建议我使用 Collections.copy() 方法。在我看过的所有示例中,目标列表都应包含要进行复制的确切数量的项目。

由于我使用的列表是通过 Web 服务填充的,并且它包含数百个对象,因此我无法使用上述技术。或者我使用的方法不正确??!!无论如何,为了使它工作,我尝试做这样的事情,但我仍然得到了 IndexOutOfBoundsException

List<SomeBean> wsList = app.allInOne(template);

List<SomeBean> wsListCopy=new ArrayList<SomeBean>(wsList.size());   
Collections.copy(wsListCopy,wsList);
System.out.println(wsListCopy.size());

我试图使用wsListCopy = wsList.subList(0, wsList.size()),但稍后在代码中我遇到了ConcurrentAccessException。尝试和错误 :)

无论如何,我的问题很简单,如何将列表的所有内容复制到另一个列表中? 当然不能通过迭代。


12
任何副本都会使用迭代,这是必然的。你可以隐藏它,但它仍将存在。 - Peter Lawrey
2
首先:你确定需要复制那个列表吗?你这么做的动机是什么? - ppeterka
2
是的,迭代只是隐藏在那些层下面。但是添加了注释以防止任何迭代答案。 :) - Mono Jamoon
@ppeterka 我正在对列表执行操作,例如removeAll()。这会导致列表丢失其原始数据。而且,“那些数据”之后也是需要的。 - Mono Jamoon
app.allInOne(template) 返回的列表的实际类型是什么?ArrayList吗? - Andremoniy
你想要一个浅拷贝还是深拷贝 - cellepo
14个回答

290

只需使用此代码:

List<SomeBean> newList = new ArrayList<SomeBean>(otherList);

注意:仍然不是线程安全的,如果您从另一个线程修改了otherList,那么您可能希望将那个otherList(甚至是newList)变为CopyOnWriteArrayList,或者使用锁原语,例如ReentrantReadWriteLock对并发访问的任何列表进行序列化读/写访问。


1
Javadoc:http://docs.oracle.com/javase/1.4.2/docs/api/java/util/ArrayList.html#ArrayList(java.util.Collection) - lcguida
7
如果他遇到了ConcurrentModificationException异常,那么他首先需要解决并发问题。 - Peter Lawrey
1
另外一件事:使用此方法时,您并不会实际复制SomeBean对象内部的List。它们仍然指向otherList内部列表。因此,如果您要编辑内部列表,请小心。 - edoardotognoni
9
如果问题提到了“复制/克隆”,为什么这个答案会得到那么多分数?尽管其他一些答案与克隆无关。使用特定于集合/流的实用方法时,集合中对象的相同引用将被保留。 - yuranos
5
答案是错误的。内容没有被复制,只有它的参考文献。 - The incredible Jan
显示剩余9条评论

44
这是一个非常好的Java 8方法来完成它:
List<String> list2 = list1.stream().collect(Collectors.toList());

当然,这里的优点是您可以过滤和跳过只复制列表的一部分。
例如:
//don't copy the first element 
List<String> list2 = list1.stream().skip(1).collect(Collectors.toList());

4
得到的列表是原始列表的深层副本还是浅层副本? - Ad Infinitum
9
浅拷贝。 - kap
4
很遗憾,这也不是线程安全的。假设在垃圾收集器运行时更改了“list”,会抛出ConcurrentModificationException异常。 - C-Otto
@Dan,如何跳过复制最后一个元素? - CKM
@chandresh 如果要跳过复制最后一个元素,只需使用.limit(list1.size() - 1) - Matthew Carpenter

24
originalArrayList.addAll(copyArrayofList);
请注意,在复制时使用 addAll() 方法时,原始数组列表和副本数组列表的内容引用相同的对象将被添加到列表中,因此如果您修改其中任何一个,则 copyArrayofList 也将反映相同的更改。如果您不希望产生副作用,则需要将 originalArrayList 中的每个元素复制到 copyArrayofList 中,例如使用 for 或 while 循环。对于深层复制,您可以使用下面的代码片段。
但是还有一件事情要做,就是实现 SomeBean 类的 Cloneable 接口并重写 clone() 方法。
public static List<SomeBean> cloneList(List<SomeBean> originalArrayList) {
    List<SomeBean> copyArrayofList = new ArrayList<SomeBean>(list.size());
    for (SomeBean item : list) copyArrayofList.add(item.clone());
    return copyArrayofList;
}

3
这是这里的少数几个真正的答案之一,因为它指定 #addAll 方法创建浅拷贝,并提供了如何进行深拷贝的方法。更多细节请见: https://dev59.com/kXRB5IYBdhLWcg3wHkPi - cellepo

11

Java 10开始:

List<E> oldList = List.of();
List<E> newList = List.copyOf(oldList);

List.copyOf() 方法返回一个包含给定 Collection 元素且不可修改的 List

给定的 Collection 不得为 null,且不得包含任何 null 元素。

如果您想创建一个 List 的深拷贝,可以在这里找到许多很好的答案。


8
我尝试过类似的操作,但仍然收到了IndexOutOfBoundsException异常。 我遇到了ConcurrentAccessException异常。
这意味着您正在尝试在复制列表时修改它,很可能是在另一个线程中。 要解决此问题,您必须:
- 使用专为并发访问设计的集合。 - 适当地锁定集合,以便您可以对其进行迭代(或允许您调用一个为您执行此操作的方法)。 - 找到一种避免需要复制原始列表的方法。

5
在Java 8中有一种以空值安全的方式处理问题的方法。
List<SomeBean> wsListCopy = Optional.ofNullable(wsList)
    .map(Collection::stream)
    .orElseGet(Stream::empty)
    .collect(Collectors.toList());

如果你想跳过一个元素。
List<SomeBean> wsListCopy = Optional.ofNullable(wsList)
    .map(Collection::stream)
    .orElseGet(Stream::empty)
    .skip(1)
    .collect(Collectors.toList());

从Java 9开始,可以使用Optional的stream方法

Optional.ofNullable(wsList)
    .stream()
    .flatMap(Collection::stream)
    .collect(Collectors.toList())

2
我尝试了类似的操作,并成功重现了问题(IndexOutOfBoundsException)。以下是我的发现:
1)Collections.copy(destList, sourceList)方法首先通过调用size()方法检查目标列表的大小。由于size()方法总是返回列表中的元素数量(在这种情况下为0),因此构造函数ArrayList(capacity)只确保支持数组的初始容量,而这与列表的大小没有任何关系。因此我们总是会遇到IndexOutOfBoundsException。
2)一个相对简单的方法是使用以集合为参数的构造函数:
List<SomeBean> wsListCopy=new ArrayList<SomeBean>(wsList);  

1

我遇到了相同的问题ConcurrentAccessException,我的解决方案是:

List<SomeBean> tempList = new ArrayList<>();

for (CartItem item : prodList) {
  tempList.add(item);
}
prodList.clear();
prodList = new ArrayList<>(tempList);

所以它一次只能执行一个操作,并避免异常...


1
你可以使用addAll()。
例如:wsListCopy.addAll(wsList);

0

如果您使用Lombok:

请在SomeBean上标记以下注释:

@Builder(toBuilder = true, builderMethodName = "")

而 Lombok 将使用复制构造函数为您执行对象的浅层复制:

inputList.stream()
         .map(x -> x.toBuilder().build())
         .collect(Collectors.toList());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接