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

基于java.util.List中元素数量的异常行为,java,list,collections,iterator,concurrentmodification,Java,List,Collections,Iterator,Concurrentmodification,我知道,当某个线程使用迭代器遍历集合时,如果该集合将被更改,迭代器.next()将抛出一个错误 但它会根据列表中元素的数量显示不同的行为 我尝试了一个代码片段,其中我在for each循环中遍历了一个列表,在遍历过程中,使用列表的remove()方法从列表中删除了一个元素 理想情况下,在这种情况下,它应该抛出ConcurrentModificationException,而不依赖于列表中的元素数,但当列表中的元素数为两个时,则不是这样 案例1:列表中的元素数-1 public static v

我知道,当某个线程使用迭代器遍历集合时,如果该集合将被更改,迭代器.next()将抛出一个错误

但它会根据列表中元素的数量显示不同的行为

我尝试了一个代码片段,其中我在for each循环中遍历了一个列表,在遍历过程中,使用列表的remove()方法从列表中删除了一个元素

理想情况下,在这种情况下,它应该抛出ConcurrentModificationException,而不依赖于列表中的元素数,但当列表中的元素数为两个时,则不是这样

案例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);
        }
    }
publicstaticvoidmain(字符串[]args)
{
列表=新的ArrayList();
列表。添加(“一”);
用于(字符串:列表)
{
System.out.println(字符串);
列表。删除(字符串);
}
}
输出:一个

线程“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);
        }
    }
publicstaticvoidmain(字符串[]args)
{
列表=新的ArrayList();
列表。添加(“一”);
列表。添加(“两个”);
用于(字符串:列表)
{
System.out.println(字符串);
列表。删除(字符串);
}
}
产出:1

未引发异常?????

案例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);
        }
    }
publicstaticvoidmain(字符串[]args)
{
列表=新的ArrayList();
列表。添加(“一”);
列表。添加(“两个”);
列表。添加(“三”);
用于(字符串:列表)
{
System.out.println(字符串);
列表。删除(字符串);
}
}
输出:一个

线程“main”java.util.ConcurrentModificationException中出现异常

再次抛出一个异常,这是理想的行为


但是为什么它在案例2中正常运行而不引发任何ConcurrentModificationException。

从列表中删除指定元素时,列表似乎不知道其大小已更改

试试这个:

从基础集合中移除由返回的最后一个元素 迭代器(可选操作)。此方法只能调用一次 每次呼叫下一个。如果 在迭代过程中修改基础集合 以调用此方法以外的任何方式

从(我的)重点:

此类的
迭代器
列表迭代器
方法是快速失败的:如果列表在任何时候进行了结构修改 创建迭代器后的时间,以任何方式,除了通过 迭代器自己的
remove
add
方法,迭代器将抛出
ConcurrentModificationException
。因此,面对同时发生的冲突, 修改后,迭代器会快速、干净地失败,而不是 在不确定的时间冒任意、不确定行为的风险 将来

请注意,无法保证迭代器的快速失败行为 一般来说,不可能作出任何硬性保证 在存在非同步并发修改的情况下快速失败 迭代器尽最大努力抛出
ConcurrentModificationException
基础。
因此,编写依赖 关于其正确性的例外:的fail fast行为 迭代器只能用于检测bug


先前发布的答案向您展示了相关文档,解释了为什么这是适当的行为;您不一定会收到该例外情况

如果您真的想知道为什么不使用两个元素来接收它(或者,如果您删除了下一个元素,而不管大小),您可以查看

您的循环:

for (String string : list)
实际上是:

for(Iterator<String> i = list.iterator(); i.hasNext(); ) {
    String string = i.next();
    ...
}
for(迭代器i=list.Iterator();i.hasNext();){
String String=i.next();
...
}
在Arraylist内部有一个
int size
,表示
Arraylist
中当前的元素数。当您调用
remove()
时,它将递减

迭代器
中有一个
int光标
,它表示
迭代器的当前位置(索引)。调用
next()

迭代器中的
hasNext()
根据大小检查当前光标位置

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

  • 光标
    0开始
    大小
    2开始
  • 调用
    next()
    cursor
    递增为
    1
  • 调用
    remove()
    size
    递减为
    1
  • hasNext()
    cursor
    size
    进行比较,发现两者相同,返回
    false
  • 循环在不引发异常的情况下退出
因此,如果在迭代过程中删除任意大小
ArrayList
中的倒数第二个元素,则不会收到异常。(同样值得注意的是,您永远不会处理循环中该列表中的最后一个元素;因此,您的示例只打印一个
One

但请记住,这是一个实现细节,不能保证。文档告诉您,您不应该依赖正在抛出(或未抛出)的异常
ArrayList
可以以不同的方式重写,其中上述内容不再适用,并且抛出异常(事实上,在另一个JVM中很可能会发生异常)