我正在阅读有关ConcurrentModificationException以及如何避免它的信息。找到an article。该文章中的第一个列表的代码类似于以下内容,这显然会导致异常:
List<String> myList = new ArrayList<String>();
myList.add("January");
myList.add("February");
myList.add("March");
Iterator<String> it = myList.iterator();
while(it.hasNext())
{
String item = it.next();
if("February".equals(item))
{
myList.remove(item);
}
}
for (String item : myList)
{
System.out.println(item);
}
然后它继续解释如何用各种建议解决问题。
当我尝试重现它时,我没有得到例外! 为什么我没有得到例外?
答案 0 :(得分:9)
根据Java API文档Iterator.hasNext不会抛出ConcurrentModificationException
。
检查"January"
和"February"
后,从列表中删除一个元素。调用it.hasNext()
不会抛出ConcurrentModificationException
但返回false。因此,您的代码干净利落地退出。但是从不检查最后一个String。如果您将"April"
添加到列表中,则会获得预期的异常。
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public class Main {
public static void main(String args[]) {
List<String> myList = new ArrayList<String>();
myList.add("January");
myList.add("February");
myList.add("March");
myList.add("April");
Iterator<String> it = myList.iterator();
while(it.hasNext())
{
String item = it.next();
System.out.println("Checking: " + item);
if("February".equals(item))
{
myList.remove(item);
}
}
for (String item : myList)
{
System.out.println(item);
}
}
}
答案 1 :(得分:3)
来自ArrayList
来源(JDK 1.7):
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;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ArrayList
上的每个修改操作都会增加modCount
字段(自创建以来修改列表的次数)。
创建迭代器时,它会将modCount
的当前值存储到expectedModCount
中。逻辑是:
modCount == expectedModCount
remove()
方法修改,modCount
会递增,但expectedModCount
也会递增,因此modCount == expectedModCount
仍然存在modCount
会递增,因此modCount != expectedModCount
会导致ConcurrentModificationException
但是,正如您从源中看到的那样,检查不会在hasNext()
方法中执行,只能在next()
中执行。 hasNext()
方法也只将当前索引与列表大小进行比较。当您从列表中删除倒数第二个元素("February"
)时,这导致以下调用hasNext()
只返回false
并终止迭代,然后CME就可以了抛出。
但是,如果您删除了倒数第二个以外的任何元素,则会抛出异常。
答案 2 :(得分:1)
我认为正确的解释是来自ConcurrentModificationExcetion的javadocs的摘录:
请注意,通常情况下无法保证快速失败的行为 说话,在场的情况下不可能做出任何硬性保证 不同步的并发修改。失败快速操作投掷 ConcurrentModificationException是尽力而为的。因此,它 编写一个依赖于此异常的程序是错误的 它的正确性:应该只使用ConcurrentModificationException 检测错误。
因此,如果迭代器快速失败,它可能抛出异常,但不能保证。尝试在示例中将February
替换为January
,并抛出异常(至少在我的环境中)
答案 3 :(得分:0)
迭代器检查它已经迭代了多少次,因为在检查并发修改之前它已经到了结尾。这意味着如果仅删除第二个最后一个元素,则在同一个迭代器中看不到CME。