Javaで開発をしていると、時々「ConcurrentModificationException」というエラーに遭遇することがあります。このエラーは、コレクションの反復処理中に同時変更が行われた場合に発生し、多くの開発者を悩ませています。本記事では、このエラーの原因と効果的な解決策について詳しく解説します。

ConcurrentModificationExceptionとは

ConcurrentModificationExceptionは、Javaのコレクションフレームワークにおいて、イテレーション中にコレクションの構造が変更された場合に発生する例外です。主に、for-eachループやイテレータを使用してコレクションを処理している最中に、要素の追加や削除を行った際に発生します。

エラーが発生する典型的な状況

以下のようなコードでエラーが発生することがよくあります:

List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");

for (String fruit : list) {
    if (fruit.equals("Banana")) {
        list.remove(fruit);  // ここでエラーが発生
    }
}

解決策

1. イテレータを使用する方法:

イテレータの`remove()`メソッドを使用することで、安全に要素を削除できます。

```java

Iterator iterator = list.iterator();

while (iterator.hasNext()) {

String fruit = iterator.next();

if (fruit.equals("Banana")) {

iterator.remove(); // 安全に削除

}

}

```

2. ConcurrentModificationに対応したコレクションを使用する:

`java.util.concurrent`パッケージの`CopyOnWriteArrayList`や`ConcurrentHashMap`などを使用することで、同時変更に対応できます。

```java

List list = new CopyOnWriteArrayList<>();

// 以下、通常のfor-eachループで安全に削除可能

```

3. 削除対象を別のリストで管理する:

削除したい要素を別のリストに追加し、ループ終了後にまとめて削除する方法です。

```java

List toRemove = new ArrayList<>();

for (String fruit : list) {

if (fruit.equals("Banana")) {

toRemove.add(fruit);

}

}

list.removeAll(toRemove);

```

4. Stream APIを使用する:

Java 8以降では、Stream APIを使用して要素のフィルタリングと削除を行うことができます。

```java

list = list.stream()

.filter(fruit -> !fruit.equals("Banana"))

.collect(Collectors.toList());

```

まとめ

ConcurrentModificationExceptionは、コレクションの同時変更によって引き起こされる一般的なエラーです。適切な解決策を選択することで、このエラーを回避し、より安全で効率的なコードを書くことができます。イテレータの使用、並行処理対応のコレクションの利用、または Stream API の活用など、状況に応じて最適な方法を選んでいくことが重要です。これらの技術を理解し適切に適用することで、より堅牢なJavaアプリケーションの開発が可能になります。