すごく渋い記事なんですが、どうしてConcurrentModificationExceptionがどのように起こるのか簡単にメモしておきます。
ArrayListなどが継承しているAbstractListクラスにはprotected transient int modCount = 0;
というフィールドがあります。
ArrayListのremove()などにはこのフィールドをインクリメントする処理が書いてあります。
public E remove(int index) {
...
modCount++;
// 削除の処理
...
}
またArrayListのforEach()などのメソッドを見ると以下のように最初にmodCountをローカルの変数に保存して、それがループごとに変わっていないかをチェックし、変わっていたらConcurrentModificationExceptionを投げるだけです。
@Override
public void forEach(Consumer<? super E> action) {
// ローカルの変数に保存
final int expectedModCount = modCount;
...
// 毎ループmodCountがローカルの変数から変わっていないかを確認して変わっていたらループをやめる
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
// 違ったらConcurrentModificationExceptionを投げる
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}