神圣的ConcurrentModificationException数

and*_*dyf 3 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);
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么删除"b"可以,但其他人NG?

Flo*_*yle 7

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

for (String s : list) {
    // Do something with s
}
Run Code Online (Sandbox Code Playgroud)

相当于:

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

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

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

    public boolean hasNext() {
            return cursor != size();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 完成的第一件事是在我们迭代时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();
    }
    
    Run Code Online (Sandbox Code Playgroud)

因此,当您迭代并删除列表的倒数第二个元素时,下一条指令将是一个调用hasNext(),它将返回false,因为删除一个元素导致列表的大小减少一个,并且您的迭代将停止而不调用next()扔一个Exception.

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