我正在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?
首先要知道的是(如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()在迭代时从列表中删除元素.