【问题标题】:Collections remove method doesn't give Concurrent Modification Exception集合删除方法不给出并发修改异常
【发布时间】:2014-06-24 00:20:53
【问题描述】:

我读过一篇关于从 this link 中删除集合中元素的文章

根据我的理解,迭代器删除方法可以防止并发修改异常,然后删除 Collection 的方法。但是当我尝试运行下面的代码时,我无法获得 concurrentmoficationexception

     List dayList= new ArrayList();
     dayList.add("Sunday");
     dayList.add("Monday");
     dayList.add("Tuesday");
     dayList.add("Wednesday");
     dayList.remove("Tuesday");
     Iterator itr=dayList.iterator();
        while(itr.hasNext())
        {
           Object testList=itr.next();
           if(testList.equals("Monday"))
             {
            dayList.remove(testList);

             }
    }
    System.out.println(dayList);
}
  • 根据 javadoc,当我们在迭代期间尝试进行任何修改时,会引发 ConcurrentModicationException。我正在使用集合删除方法,但仍然没有异常。但是如果我评论行 dayList.remove("Tuesday") ;,抛出异常。

谁能解释这段代码背后发生了什么?

【问题讨论】:

  • 您的描述不准确。如果您取消注释这两行,您只会得到例外。
  • 查看 ConcurrentModificationException 文档:docs.oracle.com/javase/7/docs/api/java/util/…
  • @EJP 即使他将iterator.remove() 行注释掉,再次调用iterator.next() 时仍然会出现异常。
  • if(){ } 是你的救星。也许JVM认为dayList.remove(testList);是安全的 :) 。 - “Fail-fast 操作会尽最大努力抛出 ConcurrentModificationException。”
  • 我已经编辑了问题的描述。谁能解释一下快速失败的行为。

标签: java collections


【解决方案1】:

添加一个Thursday,您将再次开始收到异常:)

List dayList= new ArrayList();
dayList.add("Sunday");
dayList.add("Monday");
dayList.add("Tuesday");
dayList.add("Wednesday");
dayList.add("Thursday");    // Added by Kuldeep
dayList.remove("Tuesday");
Iterator itr=dayList.iterator();
while(itr.hasNext())
{
    Object testList=itr.next();
    if(testList.equals("Monday"))
    {
        dayList.remove(testList);
    }
}
System.out.println(dayList);

所以本质上这是因为 迭代器的快速失败行为无法得到保证。快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。 查看documentation here

编辑:在您的情况下未引发异常是因为在以下代码中:

    if(testList.equals("Monday"))
    {
        dayList.remove(testList);
    }

您正在删除倒数第二个元素,这实际上将大小或 dayList3 更改为 2。所以itr.hasNext()Monday被从列表中删除后返回false,因此不会进入最后一个条目的循环,即Wednesday并避免调用itr.next()。您可以尝试调试代码以验证此行为。

【讨论】:

  • 当用户在迭代过程中试图修改Collection时抛出ConcurrentModificationException,那么为什么代码没有抛出异常以及dayList.remove("Tuesday");有所作为...里面的代码足以抛出异常
  • 那是因为fail-fast behavior cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throw ConcurrentModificationException on a best-effort basis. 请阅读 ConcurrentModificationException 的文档。
  • 我明白,但是 dayList.add("Wednesday");和 dayList.add("星期四");在迭代之前完成,这是如何与异常联系起来的,根据链接javarevisited.blogspot.in/2014/01/…,在我的情况下它应该抛出异常。
  • 是的,我调试了它,但是为什么 itr.hasNext() 返回 false ....删除星期一后,列表仍然有元素 Sunday,Wednesday。
  • 如果我评论 dayList.remove("Tuesday") 行,它会在 Object testList=itr.next(); 行抛出异常;
【解决方案2】:

如果我注释行 dayList.remove("Tuesday");,就会抛出异常......

其实这不是问题。问题是仅中间值发生异常。

“for each”循环的工作原理如下,

1.It gets the iterator.
2.Checks for hasNext().
public boolean hasNext() 
{
      return cursor != size(); // cursor is zero initially.
}
3.If true, gets the next element using next().

public E next() 
{
        checkForComodification();
        try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
        } catch (IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
        }
}

final void checkForComodification() 
{
    // Initially modCount = expectedModCount (our case 5)
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

重复步骤 2 和 3 直到 hasNext() 返回 false。

如果我们从列表中删除一个元素,它的大小会减小,而 modCount 会增加。

如果我们在迭代时移除一个元素,modCount != expectedModCount 会得到满足并抛出 ConcurrentModificationException。

但是删除倒数第二个对象很奇怪。让我们看看它在您的情况下是如何工作的。

最初,

cursor = 0 size = 5 --> hasNext() succeeds and next() also succeeds without exception.
cursor = 1 size = 5 --> hasNext() succeeds and next() also succeeds without exception.
cursor = 2 size = 5 --> hasNext() succeeds and next() also succeeds without exception.
cursor = 3 size = 5 --> hasNext() succeeds and next() also succeeds without exception.

在您的情况下,当您删除 'd' 时,大小会减小到 4。

cursor = 4 size = 4 --> hasNext() does not succeed and next() is skipped.

在其他情况下,会抛出 ConcurrentModificationException 作为 modCount != expectedModCount.

在这种情况下,不会进行此检查。

如果您尝试在迭代时打印元素,则只会打印四个条目。最后一个元素被跳过。

您的问题类似于this 问题。

【讨论】:

    【解决方案3】:

    无法保证迭代器的快速失败行为。快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。迭代器可能会也可能不会检测到无效使用。因此,编写一个依赖此异常的正确性的程序是错误的:迭代器的快速失败行为应仅用于检测错误。

    【讨论】:

    • 我希望看到文档指定集合应该努力抛出 ConcurrentModificationException 在并发修改会使迭代器无法保证某些“理智”行为的情况下,但在在集合可以正常运行的情况下,这样做没有任何问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-19
    相关资源
    最近更新 更多