它不会抛出exceptionConcurrentModificationException
我有下面的代码,我希望它抛出一个ConcurrentModificationException
,但它运行成功。 为什么会这样?
public void fun(){ List lis = new ArrayList(); lis.add(1); lis.add(2); for(Integer st:lis){ lis.remove(1); System.out.println(lis.size()); } } public static void main(String[] args) { test t = new test(); t.fun(); }
List
上的remove(int)
方法删除指定位置的元素。 在开始循环之前,列表如下所示:
[1, 2]
然后在列表上启动一个迭代器:
[1, 2] ^
你的for
循环然后删除位置1的元素 ,即数字2:
[1] ^
迭代器在下一个隐含的hasNext()
调用中返回false
,循环终止。
如果向列表中添加更多元素,则会收到ConcurrentModificationException
。 然后隐含的next()
将抛出。
请注意,来自JCF的Javadoc for ArrayList
:
请注意,迭代器的快速失败行为无法得到保证,因为一般来说,在存在不同步的并发修改时,不可能做出任何硬性保证。 失败快速迭代器会尽最大努力抛出
ConcurrentModificationException
。 因此,编写依赖于此exception的程序以确保其正确性是错误的: 迭代器的快速失败行为应该仅用于检测错误 。
这实际上可能是Oracle ArrayList
迭代器实现中的一个错误; hasNext()
不检查修改:
public boolean hasNext() { return cursor != size; }
它不会抛出ConcurrentModificationException,因为正如vandale所说,迭代器只检查next()上的编码。 这是ArrayList返回的Iterator实例的一部分:
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]; }
hasNext()只是查看光标是否指向列表的最后一个索引。 它不检查列表是否被修改。 因此,您没有得到ConcurrentModificationException,它只是停止迭代。
如果您有3个列表,如:
lis.add(1); lis.add(2); lis.add(3);
在你的情况下你会得到ConcurrentModificationException。 PS:我试过这个!
因为您没有删除1,所以在1处remove(int)
元素。( remove(int)
vs remove(Object)
)
迭代器只会检查对next()
而不是hasNext()
,并且在调用hasNext()
之后循环将退出,因为你已经删除了2,列表只有一个长,因此退出。
问题的要点是,如ArrayList
和ConcurrentModificationException
:
请注意,迭代器的快速失败行为无法得到保证,因为一般来说,在存在不同步的并发修改时,不可能做出任何硬性保证。 失败快速迭代器会尽最大努力抛出ConcurrentModificationException。
现在来自ArrayList
返回的Iterator
的代码示例:
public boolean hasNext() { return cursor != size; } public E next() { checkForComodification(); return ; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
正如您可以清楚地看到的,在ArrayList
的情况下,“尽力而为”是在调用next()
时检查修改而不是在调用getNext()
。 你的循环终止而没有第二次调用next()
,因此没有exception。 如果你有3个元素开始,或添加一个元素,它将失败。 另外值得注意的是,如果使用reflection修改数组列表而不更新modCount
变量(顽皮…),则根本不会抛出exception。 modCount
也不是volatile,它再次显示它是唯一的尽力而且无法保证,因为迭代器可能无论如何都看不到最新的值。
在这个循环中:
for(Integer st:lis){ lis.remove(1); System.out.println(lis.size()); }
你只是不断地从矩阵中删除索引为1的元素,甚至没有关注st中的内容。 所以这个循环和每次迭代都会尝试删除索引为1的项目。对此循环进行Concurent修改:
for(Integer st:lis){ lis.remove(st); System.out.println(lis.size()); }
列表中只有2个条目。 因此,循环只运行一次,因为您删除了一个条目。
如果修改了列表,则会抛出ConcurrentModificationException,并再次尝试对其执行某些操作。 但在对它进行任何操作之前,我们已经不在循环中,因此也不例外。 尝试在列表中添加另一个条目并运行将抛出exception的程序。