它不会抛出异常ConcurrentModificationException

时间:2014-07-03 14:28:20

标签: java iterator

我有以下代码,我希望它会抛出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();
}

7 个答案:

答案 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)

问题的要点是ArrayListConcurrentModificationException所述:

  

请注意,迭代器的快速失败行为无法得到保证,因为一般来说,在存在非同步并发修改的情况下,不可能做出任何硬性保证。失败快速的迭代器会尽最大努力抛出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,并再次尝试对其执行某些操作。但在对它进行任何操作之前,我们已经不在循环中,因此也不例外。尝试将另一个条目添加到列表中并运行将抛出异常的程序。