java.util.List的异常行为基于其中的元素数量

Ank*_*thi 8 java collections iterator list concurrentmodification

我知道如果使用迭代器在某个线程遍历它时将更改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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

输出:一

线程"main"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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

输出:一

没有例外被抛出?????????

案例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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

输出:一

线程"main"java.util.ConcurrentModificationException中的异常

抛出异常,这是理想的行为.

但是为什么它在case-2中正常运行而不抛出任何ConcurrentModificationException.

aba*_*aba 7

文档(强调我的):

这个类iteratorlistIterator 方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己removeadd方法之外,迭代器将抛出一个 ConcurrentModificationException.因此,在并发修改的情况下,迭代器快速而干净地失败,而不是在未来的未确定时间冒任意,非确定性行为的风险.

请注意,迭代器的故障快速行为无法得到保证,因为一般来说,在存在不同步的并发修改时,不可能做出任何硬性保证.快速失败的迭代器会ConcurrentModificationException尽力而为.因此,编写依赖于此异常的程序以确保其正确性是错误的:迭代器的快速失败行为应该仅用于检测错误.


Bri*_*ach 7

之前的答案显示了相关文档,解释了为什么这是适当的行为; 你不能保证会收到这个例外.

如果你真的很想知道为什么你只用两个元素就不会收到它(或者实际上,如果你删除最后一个元素,无论大小如何),你可以查看源代码ArrayList.

你的循环:

for (String string : list)
Run Code Online (Sandbox Code Playgroud)

实际上是:

for(Iterator<String> i = list.iterator(); i.hasNext(); ) {
    String string = i.next();
    ...
}
Run Code Online (Sandbox Code Playgroud)

在Arraylist内部,有一个int size代表当前元素的数量ArrayList.当你打电话时它会递减remove().

里面Iterator有一个int cursor代表当前位置(索引)的Iterator.当你打电话时,它会增加next()

hasNext()Iterator检查当前光标位置与大小.

在您的示例中,事件链如下所示:

  • cursor从...开始0,size从...开始2
  • next()被调用,cursor递增到1.
  • remove()被称为,size递减到1
  • hasNext()比较cursorsize,认定它们是相同的,退货false
  • 循环退出而不抛出异常

因此,如果ArrayList在迭代它时删除任何大小的倒数第二个元素,则不会收到异常.(另外值得注意的是,您永远不会处理循环中该列表中的最后一个元素;您的示例仅One出于此原因打印).

请记住 - 这是一个实现细节,不能保证.文档告诉您,您不应该依赖抛出(或不抛出)异常.ArrayList可以以不同的方式被改写,其中上面不再适用,除了抛出(事实上,在其他JVM可能也已经是这种情况).