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

我有一个从 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后来的代码。命中和试验。 :)

总之,我的问题很简单,当然不是通过迭代。

309867 次浏览

用这个:

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

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

我尝试做这样的事情,但我仍然得到一个 IndexOutOfBoundsException。

我得到一个 ConcurrentAccessException

这意味着在尝试复制列表时正在修改列表,很可能是在另一个线程中。要解决这个问题,你必须

  • 使用为并发访问而设计的集合。

  • 适当地锁定集合,以便您可以对它进行迭代(或允许您调用为您执行此操作的方法)

  • 找一个方法避免复制原始列表。

我尝试了类似的方法,并且能够重现这个问题(IndexOutOfBoundsException)。以下是我的发现:

1) Collections.copy (destList,sourceList)的实现首先通过调用 size ()方法检查目标列表的大小。由于 size ()方法的调用总是返回列表中元素的数量(在本例中为0) ,所以构造函数 ArrayList (吞吐量)只确保支持数组的初始容量,而这与列表的大小没有任何关系。因此,我们总是得到 IndexOutOfBoundsException。

2)一个相对简单的方法是使用以一个集合为参数的构造函数:

List<SomeBean> wsListCopy=new ArrayList<SomeBean>(wsList);

Re: indexOutOfBoundsException,您的子列表参数是问题所在; 您需要在大小为1时结束子列表。由于从零开始,列表的最后一个元素总是 size-1,因此大小位置中没有元素,所以出现了错误。

SubList 函数是一个技巧,返回的对象仍然在原始列表中。 因此,如果在 subList 中执行任何操作,都会导致代码中出现并发异常,无论是单线程还是多线程。

我也遇到了同样的问题,我的解决办法是:

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


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

所以它一次只能运行一个操作,并且避免了异常..。

这是一个非常好的 Java8实现方法:

List<String> list2 = list1.stream().collect(Collectors.toList());

当然,这里的优点是您可以过滤并跳到列表的部分副本。

例如:。

//don't copy the first element
List<String> list2 = list1.stream().skip(1).collect(Collectors.toList());
originalArrayList.addAll(copyArrayofList);

请记住,无论何时使用 AddAll ()方法进行复制,对同一对象的数组列表的两个引用(OrigalArrayList 和 coparrayofList)的内容都将添加到列表中,因此如果您修改其中任何一个,那么 CoparrayofList 也将反映相同的更改。

如果您不想要副作用,那么您需要将每个元素从 OrigalArrayList 复制到 CopyArrayofList,就像使用 for 或 while 循环一样。对于深度复制,可以使用下面的代码段。

但是您还需要做一件事情,实现 Cloneable接口并重写 Some Bean 类的 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;
}

Java8中还有另一种方法是空安全的。

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());

在 Java9 + 中,可以使用可选的流方法

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

可以使用 addAll ()。

例句: wsListCopy.addAll(wsList);

我看不到任何正确答案。如果你想要一个深度拷贝,你必须手动迭代和拷贝对象(你可以使用复制建构子)。

您应该使用 addAll方法。它将指定集合中的所有元素追加到复制列表的末尾。这是你名单的复印件。

List<String> myList = new ArrayList<>();
myList.add("a");
myList.add("b");
List<String> copyList = new ArrayList<>();
copyList.addAll(myList);

以防你使用龙目岛:

用下面的注释标记 Some Bean:

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

而龙目岛将使用复制建构子为您执行一个浅拷贝对象:

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