为什么retainList中的retainAll抛出异常

时间:2013-07-10 07:08:19

标签: java

我使用subList Method.Now创建了一个新的ArrayList,当我尝试使用retainAll执行交集操作时抛出以下异常

retainAll()方法适用于以下代码

List<Integer> arrNums1 = new ArrayList<Integer>();
arrNums1.add(1);
arrNums1.add(2);
arrNums1.add(3);

List<Integer> arrNums2 = arrNums1.subList(0, 1);
arrNums2.retainAll(arrNums1);

但是当我尝试将retainAll应用于Below代码时,它会生成Exception as Below

Java代码

public class Generics1
{   
 public static void main(String[] args)
 {
       List<Fruits> arrFruits = new ArrayList<Fruits>();

        Fruits objApple  = new Apple();
        Fruits objOrange = new Orange();
        Fruits objMango  = new Mango();

        arrFruits.add(objApple);
        arrFruits.add(objOrange);
        arrFruits.add(objMango);

        List<Fruits> arrNewFruits = arrFruits.subList(0, 1);

        System.out.println(arrFruits.retainAll(arrNewFruits));
  }
}

class Fruits {}

class Apple extends Fruits {}

class Orange extends Fruits {}

class Mango extends Fruits {}

错误

enter image description here

3 个答案:

答案 0 :(得分:7)

使用List#subList()时:

  

返回指定fromIndex(包含)和toIndex(不包括)之间此列表部分的视图。(如果fromIndex和toIndex相等,则返回的列表为空。)返回的列表为此列表支持,因此返回列表中的非结构更改将反映在此列表中,反之亦然。返回的列表支持此列表支持的所有可选列表操作。

您可以改变其中的元素,但不能更改列表的结构。

该文件进一步说:

  

如果支持列表(即此列表)在结构上以除返回列表之外的任何方式进行修改,则此方法返回的列表的语义将变为未定义。(结构修改是那些更改此列表的大小,或以其他方式扰乱它,使得正在进行的迭代可能会产生不正确的结果。)

retainAll()函数使用迭代器删除非相交值,这会导致ConcurrentModificationException。请注意documenation所说的内容:

  

请注意,此异常并不总是表示某个对象已被另一个线程同时修改。如果单个线程发出一系列违反对象契约的方法调用,则该对象可能会抛出此异常。

制作List的副本,然后执行retainAll()

List<Fruits> arrNewFruits = new ArrayList<>(arrFruits.subList(0, 1));

答案 1 :(得分:3)

在您的两个代码示例中,您有相反的大列表和子列表。

当您在子列表上调用retainAll()时,不会进行任何修改。

这是因为子列表中的每个元素都在大列表中。

如果未进行任何修改,则不会抛出ConcurrentModificationException

您使用整数列表执行此操作。


如果您撤销订单并在大清单上调用retainAll(),它将会发生变异。

这是因为并非大型列表中的每个项目都在子列表中。

从大列表中删除元素时,会抛出ConcurrentModificationException

这是因为在迭代时不能改变列表

您可以使用水果列表执行此操作。


迭代发生在retainAll()方法中。

在您的代码中,list参数恰好引用了正被修改的相同列表。

这是因为List.subList()的工作方式:

  
    

返回指定fromIndex(包含)和toIndex(独占)之间此列表部分的视图。 (如果fromIndex和toIndex相等,则返回的列表为空。)返回的列表由此列表支持,因此返回列表中的非结构更改将反映在此列表中,反之亦然。

  

长话短说:

您不会遇到异常如果您将代码更改为:

System.out.println(arrNewFruits.retainAll(arrFruits));

更重要的是:

如果在迭代其中一个列表时有可能修改任一列表,则需要从子列表中创建一个新列表。

您可以从子列表中创建一个新列表,如下所示:

List<Foo> freshList = new ArrayList<Foo>(bigList.subList(0,2));

现在你可以迭代并改变你心中的内容!


这是ArrayList.retainAll()的实现,您可以在其中查找迭代。

答案 2 :(得分:2)

问题是arrNewFruits实际上只是arrFruits的一部分的逻辑视图。 1 为了避免错误,您需要创建一个独立的列表: / p>

List<Fruits> arrNewFruits = new ArrayList<>(arrFruits.subList(0, 1));

1 这就是为什么你可以通过在clear()上调用subList()来删除部分列表的原因 - 在另一个中可以看到对一个列表的更改。