如何将 java.util.List 复制到另一个 java.util.List
Posted
技术标签:
【中文标题】如何将 java.util.List 复制到另一个 java.util.List【英文标题】:How to copy a java.util.List into another java.util.List 【发布时间】:2012-12-28 11:44:01 【问题描述】:我有一个从 Web 服务填充的 List<SomeBean>
。我想将该列表的内容复制/克隆到相同类型的空列表中。用于复制列表的 Google 搜索建议我使用 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
。打击和审判。 :)
无论如何,我的问题很简单,如何将我的列表的全部内容复制到另一个列表中?当然不是通过迭代。
【问题讨论】:
任何副本当然都会使用迭代。你可以把它藏起来,但它仍然会在那里。 首先:您确定需要复制该列表吗?你这样做的动机是什么? 是的,迭代只是隐藏在这些层之下。但是添加了评论以防止任何迭代答案。 :) @ppeterka 我正在对列表执行操作,例如 removeAll()。这会导致列表丢失其原始数据。之后也需要“那个数据”。app.allInOne(template)
返回的列表的实际类型是什么? ArrayList
?
【参考方案1】:
就用这个吧:
List<SomeBean> newList = new ArrayList<SomeBean>(otherList);
注意:仍然不是线程安全的,如果您从另一个线程修改otherList
,那么您可能希望将otherList
(甚至newList
)设为CopyOnWriteArrayList
,例如——或使用锁原语,例如ReentrantReadWriteLock,用于序列化对同时访问的任何列表的读/写访问。
【讨论】:
现在,我只是觉得自己很愚蠢 :) 我希望这样构建它不会抛出任何ConcurrentAccessException
。
Javadoc: docs.oracle.com/javase/1.4.2/docs/api/java/util/…
+1 如果他遇到 ConcurrentModifcationException,他有一个并发问题需要先解决。
如果问题提到“复制/克隆”,为什么这个答案会得到这么多分数?这个,只要其他一些答案与克隆无关。无论您使用何种特定于集合/流的实用程序方法,都将为集合内的对象保留相同的引用。
答案是错误的。内容不会被复制。只是它的参考。【参考方案2】:
这是一种非常好的 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());
【讨论】:
结果列表是原始列表的深拷贝还是浅拷贝? 浅拷贝。 遗憾的是,这也不是线程安全的。假设在收集器运行时更改了list
,则会抛出ConcurrentModificationException
。
@Dan,如何跳过复制最后一个元素?
@chandresh 跳过复制最后一个元素,您只需使用.limit(list1.size() - 1)
【参考方案3】:
originalArrayList.addAll(copyArrayofList);
请记住,无论何时使用 addAll() 方法进行复制,对相同对象的两个数组列表(originalArrayList 和 copyArrayofList)引用的内容都将添加到列表中,所以如果您修改其中任何一个,copyArrayofList 也将反映相同的变化。
如果您不想要副作用,那么您需要将每个元素从 originalArrayList 复制到 copyArrayofList,例如使用 for 或 while 循环。对于深拷贝,您可以使用下面的代码 sn-p。
但您还需要做一件事,实现 Cloneable
接口并覆盖 SomeBean 类的 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 clone;
【讨论】:
这是这里为数不多的正确答案之一,因为它指定#addAll 进行浅拷贝,以及如何进行深拷贝。更多详情:***.com/questions/715650/…【参考方案4】:我试图做这样的事情,但我仍然得到一个 IndexOutOfBoundsException。
我遇到了 ConcurrentAccessException
这意味着您在尝试复制列表时正在修改列表,很可能是在另一个线程中。要解决这个问题,你必须要么
使用为并发访问而设计的集合。
适当地锁定集合,以便您可以对其进行迭代(或允许您调用为您执行此操作的方法)
想办法避免复制原始列表。
【讨论】:
【参考方案5】:从 Java 10 开始:
List<E> oldList = List.of();
List<E> newList = List.copyOf(oldList);
List.copyOf()
返回一个不可修改的List
,其中包含给定Collection
的元素。
给定的Collection
不能是null
,并且不能包含任何null
元素。
另外,如果您想创建List
的深层副本,您可以找到很多好的答案here。
【讨论】:
【参考方案6】:在 Java 8 中还有另一种方法是 null 安全的。
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())
【讨论】:
【参考方案7】:我遇到了同样的问题 ConcurrentAccessException,我的解决方案是:
List<SomeBean> tempList = new ArrayList<>();
for (CartItem item : prodList)
tempList.add(item);
prodList.clear();
prodList = new ArrayList<>(tempList);
所以它一次只工作一个操作并且避免了 Exeption...
【讨论】:
【参考方案8】:我尝试了类似的方法并且能够重现问题(IndexOutOfBoundsException)。以下是我的发现:
1) Collections.copy(destList, sourceList) 的实现首先通过调用 size() 方法检查目标列表的大小。由于对 size() 方法的调用将始终返回列表中的元素数(在本例中为 0),因此构造函数 ArrayList(capacity) 仅确保后备数组的初始容量,这与列表的大小。因此我们总是得到 IndexOutOfBoundsException。
2) 一种相对简单的方法是使用以集合为参数的构造函数:
List<SomeBean> wsListCopy=new ArrayList<SomeBean>(wsList);
【讨论】:
【参考方案9】:你可以使用 addAll()。
例如:wsListCopy.addAll(wsList);
【讨论】:
【参考方案10】:回复:indexOutOfBoundsException
,您的子列表参数是问题;您需要以 size-1 结束子列表。从零开始,列表的最后一个元素总是 size-1,在 size 位置没有元素,因此会出错。
【讨论】:
【参考方案11】:我看不到任何正确答案。如果你想要一个深拷贝,你必须手动迭代和复制对象(你可以使用复制构造函数)。
【讨论】:
这是这里为数不多的真实答案之一。更多详情:***.com/questions/715650/…【参考方案12】:您应该使用addAll
方法。它将指定集合中的所有元素附加到副本列表的末尾。这将是您列表的副本。
List<String> myList = new ArrayList<>();
myList.add("a");
myList.add("b");
List<String> copyList = new ArrayList<>();
copyList.addAll(myList);
【讨论】:
【参考方案13】:subList 函数是个技巧,返回的对象还在原来的列表中。 所以如果你在subList中做任何操作,无论是单线程还是多线程,都会导致你的代码并发异常。
【讨论】:
以上是关于如何将 java.util.List 复制到另一个 java.util.List的主要内容,如果未能解决你的问题,请参考以下文章
在列表迭代期间从 java.util.List 中删除元素时引发 ConcurrentModificationException? [复制]
java 如何把一个list里的相同数的出现次数输出成另一个list