我有以下代码,我希望它会抛出ConcurrentModificationException
,但它会成功运行。为什么会这样?
public void fun(){
List <Integer>lis = new ArrayList<Integer>();
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();
}
答案 0 :(得分:10)
remove(int)
上的List
方法删除指定位置的元素。在开始循环之前,列表如下所示:
[1, 2]
然后在列表中启动迭代器:
[1, 2]
^
您的for
循环会移除位置1 的元素,即数字2:
[1]
^
迭代器在下一个隐含的hasNext()
调用中返回false
,循环终止。
如果向列表中添加更多元素,您将获得ConcurrentModificationException
。然后隐含的next()
将抛出。
请注意,来自JCF的ArrayList
的Javadoc:
请注意,迭代器的快速失败行为无法得到保证,因为一般来说,在存在非同步并发修改的情况下,不可能做出任何硬性保证。失败快速的迭代器会尽最大努力抛出
ConcurrentModificationException
。因此,编写依赖于此异常的程序以确保其正确性是错误的:迭代器的故障快速行为应仅用于检测错误。
这可能实际上是Oracle ArrayList
迭代器实现中的一个错误; hasNext()
不检查修改:
public boolean hasNext() {
return cursor != size;
}
答案 1 :(得分:3)
它没有抛出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,它只是停止迭代。
答案 2 :(得分:1)
如果你有3个列表,如:
lis.add(1);
lis.add(2);
lis.add(3);
您将获得ConcurrentModificationException。 PS:我试过了!
答案 3 :(得分:1)
因为您没有删除1,所以您要删除1处的元素。(remove(int)
vs remove(Object)
)
迭代器只检查对next()
而不是hasNext()
的调用的修改,并且在调用hasNext()
后循环将退出,因为您已删除2,列表为只有一个长,因此退出。
答案 4 :(得分:1)
问题的要点是ArrayList
和ConcurrentModificationException
所述:
请注意,迭代器的快速失败行为无法得到保证,因为一般来说,在存在非同步并发修改的情况下,不可能做出任何硬性保证。失败快速的迭代器会尽最大努力抛出ConcurrentModificationException。
现在来自Iterator
返回的ArrayList
的代码示例:
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification();
<stuff>
return <things>;
}
<more methods>
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
正如您可以清楚地看到的那样,ArrayList
&#34;尽力而为&#34;在调用next()
时正在检查修改时调用getNext()
和不是。你的循环终止而没有第二次调用next()
,因此没有例外。如果你有3个元素开头,或者添加一个元素,它将失败。另外值得注意的是,如果使用反射修改数组列表而不更新modCount
变量(顽皮......),则根本不会抛出异常。 modCount
也不是易变的,这再次表明它只是尽力而为并且没有任何保证,因为迭代器可能无论如何都看不到最新值。
答案 5 :(得分:0)
在这个循环中:
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());
}
答案 6 :(得分:0)
列表中只有2个条目。因此,循环只运行一次,因为你要删除一个条目。
如果修改了列表,则会抛出ConcurrentModificationException,并再次尝试对其执行某些操作。但在对它进行任何操作之前,我们已经不在循环中,因此也不例外。尝试将另一个条目添加到列表中并运行将抛出异常的程序。