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