2013-07-10 81 views
5

我创建使用子列表Method.Now当我尝试使用中的retainAll执行交集操作它抛出异常为什么在的retainAll ArrayList中抛出

中的retainAll(以下新的ArrayList)的例外方法适用于下面的代码

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下面的代码会生成异常,如下

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

+2

在可能的情况下,没有未知关系的代码示例是最好的。例如,这只会在Fruit类中出现,还是会出现标准的Java类有相同的问题? –

+0

当列表包含一个数字说当我创建一个列表 retainAll作品gr8 –

+0

是的我使用了字符串,并测试它也工作 –

回答

3

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

当调用子列表上retainAll(),也不会发生改变。

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

如果没有发生改变,没有ConcurrentModificationException将被抛出。

你与你的整数列表做到这一点上面。


如果颠倒顺序和大名单上调用retainAll(),它会被突变。

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

当你从大名单中删除一个元素,一个ConcurrentModificationException被抛出。

这是因为你不能在迭代它的同时改变列表

你与你的水果列表做到这一点上面。


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

在你的代码,列表参数恰好引用真实被修改的同一列表。

这是由于的方式List.subList()作品:

返回指定的fromIndex(包括)元素范围,独家之间的这份名单的一部分的视图。 (如果fromIndex和toIndex相等,则返回的列表为空。)返回的列表由此列表支持,因此返回列表中的非结构化更改将反映在此列表中,反之亦然。


长话短说:

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

更重要的是:

如果你改变你的代码,这样你就不会得到一个异常

如果有一个机会,其中一个列表正在迭代时,您可能需要从子列表中创建一个新列表。

您可以从子列表这样创建一个新的列表:

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

现在,您可以遍历并发生变异,你的心脏的内容!


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

+1

+1我想说同样的话,但没有耐心去写。 – NINCOMPOOP

+0

甚至在'contains()'中的代码,检查'size'也可能是一个可能的原因:'for(int i = 0; i NINCOMPOOP

+0

当您尝试在迭代时使用'List#remove()'从List列表中移除元素时,可能会出现同样的情况。在一些边缘情况下,它的作品。 – NINCOMPOOP

7

当您使用List#subList()

返回指定的fromIndex(包括)元素范围之间的这个名单的部分视图,独家。(如果fromIndex和toIndex相等,则返回的列表为空。)返回的列表由此列表支持,因此返回列表中的非结构化更改将反映在此列表中,反之亦然。返回的列表支持此列表支持的所有可选列表操作。

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

的DOC进一步说:

通过此方法返回的列表语义变为不确定如果支持列表(即,该列表)在结构上在比通过返回的列表的其他任何形式的修改。(结构修改是指改变该列表的大小,或者以其他方式干扰它以这样的方式,在正在进行的迭代产生不正确的结果。)

retainAll()函数使用一个迭代删除不相交的值,这会导致ConcurrentModificationException。注意documenation说什么:

请注意,此异常不会始终指出对象已经由不同线程并发修改。如果单个线程发出违反对象合约的一系列方法调用,则该对象可能会抛出此异常。

充分利用List,然后副本进行retainAll()

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

我写了一个关于如何在迭代过程中导致异常的重要答案......然后我添加了一个OpenJdk实现的链接[''的retainAll()'](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.retainAll%28java.util。收集29%)。看代码,迭代并不像我想象的那么明显。 for循环足以抛出异常,还是由其他内容引起(可能是调用'System.arrayCopy()')? – jahroy

+0

我看到代码似乎'System.arrayCopy()'是罪魁祸首。 – NINCOMPOOP

2

的问题是,arrNewFruits实际上是arrFruits一部分只是一个逻辑视图。要避免错误,你需要做一个独立的列表:

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

这就是为什么你可以通过调用一个subList() —改变一个clear()删除列表中的一部分,是出现在其他。

+1

我相信你的答案,但为什么它的工作原理当我创建整数的ArrayList –

+1

@JavaBeginner - 它不会**工作使用整数,如果你在大列表上调用retainAll()'(而不是子列表)列表)。看看你的代码,这两个例子根本不一样!您可以在**小**整数列表中调用'retainAll()',但是您可以在**大**列表中调用'retainAll()'。 – jahroy

相关问题