在阅读《阿里巴巴Java开发手册》时,发现有一条关于在 foreach 循环里进行元素的 remove/add 操作的规约,具体内容如下:
错误演示
我们首先在 IDEA 中编写一个在 foreach 循环里进行 remove 操作的代码:
import java.util.ArrayList; import java.util.List; public class ForEachTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("wupx"); list.add("love"); list.add("huxy"); for (String temp : list) { if ("love".equals(temp)) { list.remove(temp); } } System.out.println(list); } }
此时执行代码,编译正确,执行成功!输出 [wupx, huxy]。
接着我们把 “love” 换成 “wupx” 或是 “huxy” 再来运行下,执行结果如下:
纳尼,居然报错了,为什么第一次运行没有报错呢?让我们一起来进行探讨吧!
追根溯源
为了研究为什么会出现这样的情况,我们可以根据异常堆栈信息,去追踪错误,其中涉及到的部分源码如下:
private class Itr implements Iterator<E> { int cursor; // 下一个要返回的元素的索引 int lastRet = -1; // 返回的最后一个元素的索引(如果没有返回-1) int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw