【Java学習|実務向け】実務でハマるJavaコレクションの削除処理:ConcurrentModificationExceptionを回避する正しい作法

1. 導入:なぜコレクションの削除処理が重要なのか

Javaの開発現場において、ListやSetから要素を削除する処理は日常的です。しかし、ループ処理中に不用意にremoveメソッドを呼び出すと、ConcurrentModificationExceptionが発生し、システムが停止するリスクがあります。本記事では、コレクション操作における「安全な削除」と「効率的な削除」のベストプラクティスを解説します。

2. 基礎知識:Javaコレクションの削除メソッド

JavaのCollectionインターフェースには、主に以下の2つの削除メソッドが存在します。

remove(Object o): 指定された要素の最初の出現を削除します。
removeAll(Collection c): 指定されたコレクションに含まれる要素をすべて削除します。

これらのメソッドは、内部的に「イテレータ」を使用して要素を走査します。そのため、拡張for文(for-eachループ)の中で直接これらのメソッドを呼び出すと、イテレータの整合性が崩れ、例外が投げられる仕様になっています。

3. 実装/解決策:安全な削除方法

実務において最も推奨されるのは、Java 8から導入されたremoveIf(Predicate filter)メソッドを使用することです。これにより、ループを自前で書く必要がなくなり、コードの可読性と安全性が劇的に向上します。

4. サンプルプログラム

以下に、Listから条件に一致する要素を安全に削除する実用的なコード例を示します。

import java.util.ArrayList;
import java.util.List;

public class CollectionRemoveExample {
public static void main(String[] args) {
List items = new ArrayList<>();
items.add(“Apple”);
items.add(“Banana”);
items.add(“Cherry”);
items.add(“Date”);

// 1. removeIfを使った安全な削除(推奨)
// 「B」で始まる要素をすべて削除する
items.removeIf(item -> item.startsWith(“B”));

// 2. removeAllを使った一括削除
// 削除対象のリストを定義
List toRemove = List.of(“Apple”, “Date”);
items.removeAll(toRemove);

// 結果を出力:Cherryのみが残る
items.forEach(System.out::println);
}
}

5. 応用・注意点:現場での落とし穴

現場で遭遇しやすい注意点を3つ挙げます。

Arrays.asList()の罠: Arrays.asList()で作成したリストは固定サイズであるため、removeメソッドを呼ぶとUnsupportedOperationExceptionが発生します。変更が必要な場合は、new ArrayList<>(Arrays.asList(…))のようにラップしてください。
Mapの削除: Map自体にはremoveIfはありませんが、entrySet()を取得してそのイテレータを操作するか、keySet().removeIf()を使用することで安全に削除可能です。
Sequenced Collections (Java 21以降): Java 21から導入されたSequencedCollectionでは、first/last要素の削除がより直感的になりました。順序が重要なリストを扱う際は、これらの新しいAPIを検討しましょう。

パフォーマンス面では、ArrayListに対して頻繁にremoveを行うと、後ろの要素を詰めるためのコピー処理が走り、計算量がO(n)になります。削除対象が多い場合は、一度で削除するのではなく「新しいリストを作成する(フィルタリング)」というアプローチも検討してください。

コメント

タイトルとURLをコピーしました