如果我写
String myFirstString = "a";
String mySecondString = "b";
List<String> lstOfStrings = new ArrayList<String>();
lstOfStrings.add(myFirstString);
lstOfStrings.add(mySecondString);
for (String value : lstOfStrings) {
if(value.equals("a")) {
lstOfStrings.remove("a");
System.out.println("removed successfully");
}
}很好,但是,
如果我更改列表中插入的顺序,它将给java.util.ConcurrentModificationException提供如下代码
String myFirstString = "a";
String mySecondString = "b";
List<String> lstOfStrings = new ArrayList<String>();
lstOfStrings.add(mySecondString);
lstOfStrings.add(myFirstString);
for (String value : lstOfStrings) {
if(value.equals("a")) {
lstOfStrings.remove("a");
System.out.println("removed successfully");
}
}给出java.util.ConcurrentModificationException
为什么每个人都会有这样的行为?我知道有许多方法像Iterator,CoppyOnWriteArraylist作为ConcurrentModificationException异常的解决方案。但我想知道这个案子的原因。请解释一下。
发布于 2014-07-17 06:16:36
我将猜测,为for (val: collection )的迭代器语法糖生成的字节代码的实现包括优化检查您是否在集合的最后一个元素上,而不需要再次进入迭代器。因此,如果您移除集合中最后一个项,它将不会抛出异常并跳过最后一个项。这在一定程度上被两个实验所证实:在你的第一个例子中添加第三项,它就会失败。修改它以删除第二项而不是第一项,并将再次完成“成功”。
更新:啊,这个问题是Why isn't this code causing a ConcurrentModificationException?的翻版
发布于 2014-07-17 06:07:43
Java循环内部使用集合的iterator。
iterator的行为是fail-fast,当集合的任何内容在循环过程中被更改时,它都会失败。
有关更多信息,我建议阅读下面的文章。
http://www.jguru.com/faq/view.jsp?EID=221988
http://www.developersfusion.com/Articles/AD/F/53/ConcurrentModificationException---Fail-Fast-and-Fail-Safe-iterators.aspx
http://docs.oracle.com/javase/6/docs/api/java/util/ConcurrentModificationException.html
发布于 2014-07-17 06:29:45
正是由于ArrayList迭代器的实现方式。
当在最后一个元素之前删除元素时,迭代器将不再迭代最后一个元素。
试着打印出每一个迭代
String myFirstString = "a";
String mySecondString ="b";
List<String> lstOfStrings = new ArrayList<String>();
lstOfStrings.add(myFirstString);
lstOfStrings.add(mySecondString);
for (String value : lstOfStrings) {
System.out.println("Iterating: " + value);
if(value.equals("a")){
lstOfStrings.remove("a");
System.out.println("removed successfully");
}
}
System.out.println(lstOfStrings);输出是
Iterating: a
removed successfully
[b]正如您在最后一个System.out.println中看到的那样,lstOfString仍然包含元素"b",但它并不是迭代的。
当您查看ArrayList的实现时,您可以看到其中的原因。
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
....
}Iterator.hasNext()只检查实际游标是否为大小。因此,如果迭代列表并删除字符串"a",光标指向第二个元素"b",但是大小已经缩小到1,因此第二个元素不再迭代。
甚至在JDK1.7中也会发生这种情况,而且我还没有测试这种行为是否在1.8中仍然存在。
当您使用Iterator.remove()方法时,它将工作,因为迭代器知道删除。
Iterator<String> iterator = lstOfStrings.iterator();
while (iterator.hasNext()) {
String value = iterator.next();
System.out.println("Iterating: " + value);
if (value.equals("a")) {
iterator.remove();
System.out.println("removed successfully");
}
}打印出来:
Iterating: a
removed successfully
Iterating: bhttps://stackoverflow.com/questions/24795963
复制相似问题