2014-05-14 52 views
2

我想从循环内的ArrayList中删除元素。从循环内的列表中删除元素

这是好的。

ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 2, 3)); 
for(Integer i: list){ 
    if(i == 2) 
     list.remove(i); 
} 

但是,这是不是和抛出ConcurrentModificationException。

ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 2, 3)); 
for(Integer i: list){ 
     list.remove(i); 
} 

我不明白为什么。

我刚添加了另一个元素,它也不行(抛出concurrentMOdificationException)。

ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4)); 

System.out.println(list); 

for (Integer i : list) { 
    if (i == 2) 
     list.remove(i); 
} 
+0

可能因为元素'2'不在'list'中。 – rgettman

+4

http://stackoverflow.com/q/8189466/738746 - 相关?再添加一个元素,它也会抛出CME。 –

+0

@rgettman不,它在列表中。 – Ryan

回答

4

使用Iterator类而不是for-each循环。

Iterator<Integer> it = list.iterator(); 
while (it.hasNext()) { 
    Integer i = it.next(); 
    it.remove(); 
} 

http://docs.oracle.com/javase/7/docs/api/java/util/ConcurrentModificationException.html

例如,它不是通常用于允许一个线程来修改集合,而另一个线程被遍历它。一般来说,在这些情况下迭代的结果是不确定的。某些迭代器实现(包括由JRE提供的所有通用集合实现的实现)可能会选择在检测到此行为时抛出此异常。这样做的迭代器被称为快速迭代器,因为它们快速且干净地失败,而在将来未定的时间冒着任意的,非确定性的行为冒险。

请注意,此异常并不总是表示某个对象已被另一个线程同时修改。如果单个线程发出违反对象合约的一系列方法调用,则该对象可能会抛出此异常。例如,如果一个线程在使用快速迭代器迭代集合的同时直接修改集合,迭代器将抛出此异常。

+2

虽然这可能是解决问题的方法,但OP也对第一个示例为何不抛出ConcurrentModificationException感兴趣。 – rgettman

+2

这就是为什么我必须保护我的问题。请首先了解问题的动机。 –

0

您必须了解一点当使用这种性质的for循环时会发生什么。它真的是在引擎盖下使用java.util.Iterator。此迭代器将使用next()方法来确定何时停止迭代,并使用hasNext()方法来检索下一个元素。

踢球者是只有next()检查并发修改 - hasNext()不执行检查。在第一种情况下,您要等到第二次循环迭代才能从列表中删除2,然后下一次迭代找到列表的末尾并退出。在第二种情况下,您在第一次迭代期间从列表中删除1,并且下一次迭代尝试检索下一个元素时会引发异常。

+1

第一个实际移除第二个元素。我不是指数,我是自动复制的元素。 – Ryan

+0

@瑞恩我有一个问题,但不是你指出的那个。 'i'是自动复制的元素,但是当您在列表中调用'remove'时,它将被视为索引。因此,当i = 2时,list.remove(i)'会移除第三个(最后一个)元素。但是,您让我仔细检查了我的答案,并且错误地告知了第二种情况下哪个元素会被移除,所以我更新了我的回答。 –

+0

在第一种情况下,你等到第二个循环迭代去除SECOND元素 – Ryan