12

我有以下两个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

我只想知道为什么同一段代码在家庭的情况下会抛出 ConcurrentModificationException 但在家庭的情况下list没有任何ConcurrentModificationExceptionset

4

6 回答 6

5

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

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

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

因此,当您hasNext在第二个循环中调用时,它(错误地)表明有更多的项目需要迭代,因为您在第一次迭代之后执行了一个更改列表大小的操作。从语义上讲,您应该无法在到达列表末尾后继续迭代列表中的项目,但是由于这个实现细节,它可以让您继续进行第二个 while 循环。当然,此时,由于您在后备列表中所做的更改,您会遇到并发修改异常。

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

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

这种实现碰巧不会像在迭代完成后对散列集所做的修改那样“脆弱”,因此该hasNext方法表现得更好。

于 2013-02-26T14:49:40.267 回答
4

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

于 2013-02-26T14:52:32.470 回答
2

首先阅读 Iterator 的JavaDoc。它有没有提到ConcurrentModificationException任何地方?

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

当这种修改是不允许的时,检测到对象的并发修改的方法可能会抛出此异常。

现在仔细查看您的代码。您的while循环遍历集合的所有元素(即使您的第一个示例的输出没有表明这一点,这告诉我您已经编辑了输出或者这不是您的实际代码)。在您删除元素时,没有更多要迭代的项目,因此第二个循环应始终立即退出。

因此,结论是列表迭代器的实现者选择了抛出异常,即使没有更多元素要迭代,而集合迭代器的实现者选择不这样做。鉴于规格,这两种情况都是完全可以接受的。

于 2013-02-26T14:51:17.593 回答
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
于 2013-02-26T14:49:24.557 回答
1

如果您对集合执行任何操作(通过迭代器除外),则 Hashset 可能会引发 ConcurrentModificationException。然而,围绕迭代器的快速失败行为有很多启发式方法,目的是尽可能完成迭代。JavaDocs 似乎很清楚它的行为。

于 2013-02-26T14:57:26.160 回答
0

如果是列表,当我们使用第一个循环遍历它时 Iterator itr = set.iterator();

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

游标值和大小将变得相同。游标包含遍历元素总数的值,并且列表遍历的 hashNext() 方法包含以下代码:

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

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

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

于 2017-12-11T08:03:12.183 回答