神圣的ConcurrentModificationException数

时间:2014-08-28 09:23:08

标签: java

我正在使用以下代码测试ConcurrentModificationException集合:

public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<String>();
    list.add("a");
    list.add("b");
    list.add("c");

    for (String s : list) {
     // if (s.equals("a")) { // ConcurrentModificationException!
        if (s.equals("b")) { // -->> Magic number, NO Exception, Why? 
     // if (s.equals("c")) { // ConcurrentModificationException!
            list.remove(s);
        }
    }
    System.out.println(list);
}

我不明白为什么要删除&#34; b&#34;没问题,但其他人呢?

1 个答案:

答案 0 :(得分:7)

首先要知道的是(如JLS中所述)以下增强的for循环:

for (String s : list) {
    // Do something with s
}

相当于:

for (Iterator<String> it = list.iterator(); it.hasNext();) {
    String s = it.next();
    // Do something with s
}

如果你看一下AbstractList中迭代器的实现,你会看到:

  • hasNext()没有检查并发修改,只是检查我们是否在列表的末尾,使用其大小:

    public boolean hasNext() {
            return cursor != size();
    }
    
  • next()完成的第一件事就是在我们迭代时调用checkForComodification()来查看列表是否被修改:

    public E next() {
            checkForComodification();
        try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
        } catch (IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
        }
    }
    
    final void checkForComodification() {
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    }
    

因此,当您迭代并删除列表中的倒数第二个元素时,下一条指令将是对hasNext()的调用,它将返回false,因为删除一个元素会导致其大小列表减少一个,并且您的迭代将在不调用next()并抛出Exception的情况下停止。

顺便说一句,所有这些只是一个实现细节,你不应该依赖它,因为它可以改变,并且在迭代时使用it.remove()从列表中删除元素。