我知道如果在某个线程使用迭代器遍历它时将更改Collection,则iterator.next()将抛出ConcurrentModificationException。
但它显示了不同的行为,具体取决于列表中的元素数量。
我尝试了一个代码片段,在其中遍历for-each循环中的列表,在它之间,遍历使用列表中的remove()方法从列表中删除了一个元素。
理想情况下,它应该在这种情况下抛出ConcurrentModificationException而不依赖于列表中的元素数量,但是当列表中的元素数量为2时,它不是真的。
案例1:列表中的元素数量 - 1
public static void main(String[] args)
{
List<String> list=new ArrayList<String>();
list.add("One");
for (String string : list)
{
System.out.println(string);
list.remove(string);
}
}
输出:一个
线程中的异常&#34; main&#34; java.util.ConcurrentModificationException
那是预期的。
案例2:列表中的元素数量 - 2
public static void main(String[] args)
{
List<String> list=new ArrayList<String>();
list.add("One");
list.add("two");
for (String string : list)
{
System.out.println(string);
list.remove(string);
}
}
输出:一个
没有抛出异常??????????
案例3:列表中的元素数量 - 3
public static void main(String[] args)
{
List<String> list=new ArrayList<String>();
list.add("One");
list.add("Two");
list.add("Three");
for (String string : list)
{
System.out.println(string);
list.remove(string);
}
}
输出:一个
线程中的异常&#34; main&#34; java.util.ConcurrentModificationException
再次引发异常,这是理想的行为。
但是为什么它在 case-2 中正常运行而不抛出任何ConcurrentModificationException。
答案 0 :(得分:7)
来自documentation(强调我的):
此类
iterator
和listIterator
返回的迭代器 方法是失败快速:如果列表在结构上被修改了 创建迭代器之后的时间,除了通过之外的任何方式 迭代器自己的remove
或add
方法,迭代器将抛出一个ConcurrentModificationException
。因此,面对并发 修改,迭代器快速而干净地失败,而不是 在不确定的时间冒着任意的,非确定性的行为 在将来。请注意,无法保证迭代器的快速失败行为 一般来说,不可能做出任何硬性保证 存在不同步的并发修改。的快速失败 迭代器会尽最大努力抛出
ConcurrentModificationException
基础。因此,编写一个依赖的程序是错误的 关于它的正确性的这个例外:的失败快速行为 迭代器只应用于检测错误。
答案 1 :(得分:7)
之前发布的答案向您展示了解释为什么这是恰当行为的相关文档;你不是保证接收该例外。
如果你真的有兴趣看到为什么你只用两个元素就不会收到它(或者真的,如果你删除最后一个元素,无论大小如何),你可以看一下在source code for ArrayList
。
你的循环:
for (String string : list)
实际上是:
for(Iterator<String> i = list.iterator(); i.hasNext(); ) {
String string = i.next();
...
}
在Arraylist内部有一个int size
代表ArrayList
中当前的元素数量。当你拨打remove()
时它会递减。
在Iterator
内,有一个int cursor
代表Iterator
的当前位置(索引)。当您致电next()
hasNext()
中的 Iterator
会根据大小检查当前光标位置。
在您的示例中,事件链如下:
cursor
从0
开始,size
从2
开始next()
被调用,cursor
增加到1
。 remove()
被调用,size
递减到1
hasNext()
将cursor
与size
进行比较,发现它们相同,返回false
因此,如果在迭代它时删除任何大小ArrayList
中的倒数第二个元素,则不会收到异常。 (另外值得注意的是,您永远不会处理循环中该列表中的最后一个元素;因为该原因,您的示例仅打印One
。
请记住 - 这是实施细节,并不能保证。文档告诉您,您不应该依赖抛出(或不抛出)异常。可以用上面不再适用的异常方式重写ArrayList
并抛出 异常(实际上,在另一个可能已经存在的JVM中)。
答案 2 :(得分:0)
似乎从列表中删除指定的元素时,列表不知道其大小是否已更改。
试试这个:
从底层集合中删除返回的最后一个元素 迭代器(可选操作)。此方法只能调用一次 每次打电话给下一个。如果是,则未指定迭代器的行为 迭代正在进行时修改底层集合 除了通过调用此方法之外的任何其他方式。