Java: ConcurrentModificationException
Javaでコレクションをイテレート中に変更した際のエラー原因と解決策
概要
コレクションをイテレート中に構造を変更した際に発生するエラーです。
エラーメッセージ
``` java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) ```
原因
- for-eachループ中の変更: 拡張forループでadd/remove
- マルチスレッドアクセス: 別スレッドからの変更
- ネストしたイテレーション: 内側で外側のコレクションを変更
- Streamでの変更: Stream処理中の元コレクション変更
解決策
1. Iterator.remove()を使用
```java List list = new ArrayList<>(Arrays.asList(“a”, “b”, “c”));
// 悪い例 for (String item : list) { if (item.equals(“b”)) { list.remove(item); // ConcurrentModificationException } }
// 良い例 Iterator iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if (item.equals(“b”)) { iterator.remove(); // OK } } ```
2. removeIfを使用 (Java 8+)
```java list.removeIf(item -> item.equals(“b”)); ```
3. 新しいコレクションを作成
```java List toRemove = new ArrayList<>(); for (String item : list) { if (item.equals(“b”)) { toRemove.add(item); } } list.removeAll(toRemove);
// または Stream List filtered = list.stream() .filter(item -> !item.equals(“b”)) .collect(Collectors.toList()); ```
4. ConcurrentHashMapを使用
```java // マルチスレッド環境 Map<String, Integer> map = new ConcurrentHashMap<>();
// for-eachでも安全 for (Map.Entry<String, Integer> entry : map.entrySet()) { if (entry.getValue() < 0) { map.remove(entry.getKey()); // OK(弱い一貫性) } } ```
5. CopyOnWriteArrayListを使用
```java List list = new CopyOnWriteArrayList<>();
// 読み取りが多く、書き込みが少ない場合に有効 for (String item : list) { if (item.equals(“b”)) { list.remove(item); // OK(コピーが作成される) } } ```
よくある間違い
- synchronizedでラップすれば安全と思い込む
- インデックスベースのforループでも発生する場合がある
- fail-fastの仕組みを理解していない
関連エラー
関連エラー
Java の他のエラー
この記事は役に立ちましたか?