【问题标题】:How to copy a java.util.List into another java.util.List如何将 java.util.List 复制到另一个 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?

标签: java collections copy


【解决方案1】:

就用这个吧:

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

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

【讨论】:

  • 现在,我只是觉得自己很愚蠢 :) 我希望这样构建它不会抛出任何ConcurrentAccessException
  • +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;
}

【讨论】:

【解决方案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】:

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

                【讨论】:

                【解决方案12】:

                您应该使用addAll 方法。它将指定集合中的所有元素附加到副本列表的末尾。这将是您列表的副本。

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

                【讨论】:

                  【解决方案13】:

                  subList 函数是个技巧,返回的对象还在原来的列表中。 所以如果你在subList中做任何操作,无论是单线程还是多线程,都会导致你的代码并发异常。

                  【讨论】:

                    猜你喜欢
                    • 2012-05-14
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-01-26
                    • 2016-05-27
                    • 2017-06-07
                    • 2017-07-03
                    • 1970-01-01
                    • 2015-09-24
                    相关资源
                    最近更新 更多