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

Rai*_*lam 11 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());
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

这是预期的,因为我在迭代时修改列表.但是在下面的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());
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

出来就是.

hibernate
core java
j2ee
php
struts
Run Code Online (Sandbox Code Playgroud)

没有任何ConcurrentModificationException.

我只是想知道为什么同一段代码在系列的情况下抛出 ConcurrentModificationExceptionlist,但在系列的情况下没有任何ConcurrentModificationExceptionset

Per*_*ion 5

这是一种"逆行"行为,因为迭代器一旦完全遍历,就不可重用,也就是说,hasNext当你到达列表末尾时,它们的方法应该返回false.

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

public boolean hasNext() {
    return cursor != size;
}
Run Code Online (Sandbox Code Playgroud)

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

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

public final boolean hasNext() {
    return next != null;
}
Run Code Online (Sandbox Code Playgroud)

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


das*_*ght 4

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