List抛出ConcurrentModificationException但是set不会抛出ConcurrentModificationException?

时间:2013-02-26 14:43:45

标签: java list arraylist set hashset

我有两个java类

import java.util.*;

public class ArrayListTest032 {
    public static void main(String[] ar) {
        List<String> list = new ArrayList<String>();
        list.add("core java");
        list.add("php");
        list.add("j2ee");
        list.add("struts");
        list.add("hibernate");

        Iterator<String> itr = list.iterator();

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
        list.remove("php");

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }

    }
}

当我运行上面的代码时,我得到低于输出。

core java
php
j2ee
struts
hibernate

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
    at java.util.AbstractList$Itr.next(AbstractList.java:343)
    at ArrayListTest032.main(ArrayListTest032.java:20)

我希望在迭代时修改列表。但是在下面的java类中,set family执行相同的逻辑。

import java.util.*;

public class HashSetTest021 {
    public static void main(String[] ar) {
        Set<String> set = new HashSet<String>();
        set.add("core java");
        set.add("php");
        set.add("j2ee");
        set.add("struts");
        set.add("hibernate");

        Iterator<String> itr = set.iterator();

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
        set.remove("php");

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }

    }
}

出来就是。

hibernate
core java
j2ee
php
struts

没有任何 ConcurrentModificationException

我只是想知道为什么同一段代码在list系列的情况下抛出 ConcurrentModificationException ,但在{{{}}的情况下没有任何 ConcurrentModificationException 1}}家庭

6 个答案:

答案 0 :(得分:5)

这是一种“逆行”行为,因为迭代器一旦完全遍历,就不能重复使用,当你到达列表末尾时,他们的hasNext方法应该返回false。

在这种情况下,ArrayList.iterator返回的迭代器是一个内部实现类,代码为hasNext,如下所示:

public boolean hasNext() {
    return cursor != size;
}

因此,当您在第二个循环中调用hasNext时,它表示(错误地)有更多项要迭代,因为您在第一次迭代后执行了更改列表大小的操作。在语义上,您应该无法在到达结尾后继续迭代列表中的项目,但由于此实现细节,它允许您继续第二个while循环。当然,此时,由于您在支持列表中所做的更改,您会收到并发修改异常。

另一方面,哈希集使用的迭代器的hasNext实现如下:

public final boolean hasNext() {
    return next != null;
}

这种实现不会像在迭代完成后对哈希集所做的修改那样“易受攻击”,因此hasNext方法的表现更好。

答案 1 :(得分:4)

这是实现上的差异:数组列表返回的迭代器检测并发修改,即使它位于最后,因为它检查长度;另一方面,HashSetTreeSetLinkedList的迭代器不会检测到这种情况,因为它们在检查并发修改之前检查是否位于末尾。该文档允许迭代器不要进行并发修改,因此两种方法都是有效的。

答案 2 :(得分:2)

首先阅读Iterator的JavaDoc。它在任何地方都提到ConcurrentModificationException吗?

现在,阅读ConcurrentModificationException的JavaDoc,并注意以下内容(强调添加):

  

在不允许进行此类修改的情况下,检测到并发修改对象的方法会抛出此异常

现在仔细查看您的代码。你的while循环遍历集合的所有元素(即使你的第一个例子的输出没有表明这一点,这告诉我你编辑了输出或者这不是你的实际代码)。删除元素时,没有其他项可以迭代,因此第二个循环应该总是立即退出。

因此,结论是列表迭代器的实现者选择即使没有更多元素要迭代也会抛出该异常,而set迭代器的实现者有选择不是。根据规格,这两种情况都是完全可以接受的。

答案 3 :(得分:1)

 public static void main(String[] ar) {
            List<String> list = new ArrayList<String>();
            list.add("core java");
            list.add("php");
            list.add("j2ee");
            list.add("struts");
            list.add("hibernate");

            Iterator<String> itr = list.iterator();

            while (itr.hasNext()) {
                System.out.println(itr.next());
            }
            list.remove("php");

          /*  while (itr.hasNext()) {
                System.out.println(itr.next());
            }*/

        }

problem in itr object.it holds the list object reference

答案 4 :(得分:1)

如果你通过迭代器对集合做任何事情,Hashset会抛出一个ConcurrentModificationException。然而,围绕迭代器的快速失败行为有很多启发式方法,目标是尽可能完成迭代。 JavaDocs似乎很清楚它的行为。

答案 5 :(得分:0)

当我们用第一个循环遍历它时,

如果是列表  Iterator itr = set.iterator();

    while (itr.hasNext()) {
        System.out.println(itr.next());
    }

游标值和大小将变为相同.Cursor包含遍历的元素总数的值,内部hashNext()方法用于列表遍历包含代码:

  public boolean hasNext() {
            return cursor != size;
        }

所以在第一次循环游标之后== size.But从列表大小中删除元素后变为(originalSize-1)。对于下一个while循环它进入内部而在itr.next()方法内部它检查modcount修改和抛出ConcurrentModificationException。

在Set的情况下,它为每个itr.hasnext()调用检查next!= null。在遍历第一个while循环后,next变为null。从set中移除元素不会影响下一个值为null并且itr.hasNext将返回next == null as true因此它不会进入while循环以检查modcount修改。因此它不会抛出ConcurrentModification异常。