请看下面的代码片段:
List<String> list = new LinkedList<>();
list.add("Hello");
list.add("My");
list.add("Son");
for (String s: list){
if (s.equals("My")) list.remove(s);
System.out.printf("s=%s, list=%s\n",s,list.toString());
}
这将导致以下输出结果:
s=Hello,list=[Hello,My,Son] s = My,list = [Hello,Son]
因此,循环明显只执行了两次,第三个元素"Son"从未被访问。从底层库代码来看,似乎迭代器中的
hasNext()
方法并没有检查并发修改,只是针对下一个索引检查大小。由于remove()
调用使大小减小1,因此循环根本不再进入,但不会抛出ConcurrentModificationException。这似乎违反了迭代器的合同:
列表迭代器是快速失败的:如果在创建Iterator之后的任何时候以除了列表迭代器自己的
remove
或add
方法之外的任何方式对列表进行结构修改,则列表迭代器将抛出ConcurrentModificationException。因此,在面对并发修改时,迭代器会迅速而干净地失败,而不是冒着在未来某个不确定的时间面临任意的、非确定性的行为风险。这是一个bug吗? 再次说明,迭代器的合同在这里明显被违反了——列表的结构在迭代过程中被其他东西修改了。
hasNext()
方法中修改检查的唯一原因是,在所有情况下都会保证检查操作翻倍,而缺少这样的检查只会导致在执行修改时仅在某些情况下不抛出异常,例如当从迭代器的当前位置到容器末尾的所有元素都被删除时立即退出循环,这被实现者认为相对较少发生。 - striving_coder