Aikido

コレクションからアイテムを削除する際、安全なパターンを使用すべき理由

読みやすさ

ルール
使用 安全な メソッド するときは 除去  コレクションから
変更 a コレクション コレクションの コレクション   しばしば バグを バグを引き起こす。

サポート言語: PY、 Java、 C/C++、 C#、 
Swift/Objective-C, Ruby, PHP、 Kotlin, Go,
Scala、 Rust、 Groovy, Dart, Julia, Elixit、 
Erlang, Clojure、 OCaml、 Lua

はじめに

反復処理中にコレクションから要素を削除すると、Javaでは並行変更例外が発生し、C#では予期しない動作を引き起こします。イテレータは内部ポインタを維持しており、基となるコレクションが変更されると無効になります。これにより、コレクションの種類や削除パターンに応じて、要素のスキップ、クラッシュ、または無限ループが発生します。

なぜそれが重要なのか

システムの安定性:同時変更例外が発生すると、アプリケーションは即座にクラッシュします。本番環境では、これはリクエストの喪失やサービスの利用不能を意味します。この例外は特定のデータを含むエッジケースで頻繁に発生するため、テスト中に捕捉することが困難です。

データ整合性:削除ロジックが反復処理の途中で失敗した場合、コレクションは部分的に変更された状態で残される。一部の項目は削除される一方、削除されるべき他の項目が残存する。これにより不整合なデータが生じ、下流のロジックに影響を及ぼす。

デバッグの複雑性:並行変更バグはタイミング依存であり、特定のデータ組み合わせでのみ発生する可能性がある。一貫して再現することが困難なため、信頼性のあるデバッグと修正が難しい。

コード例

非準拠:

List<User> users = getUserList();
for (User user : users) {
    if (!user.isActive()) {
        users.remove(user); // ConcurrentModificationException
    }
}

なぜそれが間違っているのか: 除去する ユーザー 拡張forループを用いた反復処理を行うと 同時変更例外イテレータは、コレクションがイテレータの外で変更されたことを検出し、直ちに例外をスローします。最初の非アクティブユーザー以降のアクティブユーザーは一切処理されません。

✅ 準拠:

List<User> users = getUserList();
Iterator<User> iterator = users.iterator();
while (iterator.hasNext()) {
    User user = iterator.next();
    if (!user.isActive()) {
        iterator.remove(); // Safe removal through iterator
    }
}

なぜこれが重要なのか: 使用 イテレータの削除 反復処理中に安全に項目を削除します。反復処理は一貫した状態を維持し、残りの項目の処理を継続します。すべての非アクティブユーザーは例外なく正しく削除されます。

結論

イテレータを使用する remove() 反復処理中の安全な削除方法。あるいは、ストリームを使用して filter() 新しいコレクションを作成する、または removeIf() 大量廃棄用。回収業者には絶対に電話しないでください。 remove() 反復処理中に直接

よくある質問

ご質問は?

インデックス付きの通常のforループを使うのはどうですか?

インデックスを用いた逆方向の反復処理は機能する:for (int i = list.size() - 1; i >= 0; i--)。要素を削除すると後続の要素がずれるが、逆方向の反復処理によりスキップを回避できる。ただし、iterator.remove() や removeIf() の方が明確でエラーが発生しにくい。

代わりにJava 8+のremoveIfを使用できますか?

はい、users.removeIf(user -> !user.isActive()) が推奨される現代的なアプローチです。より簡潔で、内部で反復処理を安全に処理します。述語に基づく削除にはremoveIf()を、変換にはストリームを、削除ロジックが複雑な場合にはイテレータメソッドを使用してください。

これはすべてのコレクションタイプに適用されますか?

はい、ArrayList、HashSet、HashMap、およびほとんどのコレクションは、反復処理中に変更されるとConcurrentModificationExceptionをスローします。ConcurrentHashMapのようなスレッドセーフなコレクションは変更を許可しますが、異なるセマンティクスを持ちます。変更ルールについては、常にコレクションのドキュメントを確認してください。

C#のコレクションについてはどうですか?

C#では、コレクションを反復処理中に変更するとInvalidOperationExceptionが発生します。反復処理前にToList()でコピーを作成してください:foreach (var user in users.ToList()) その後、元のコレクションから削除します。またはLINQを使用します:users = users.Where(u => u.IsActive).ToList().

複数のアイテムを効率的に削除するにはどうすればよいですか?

単一のコレクションにはremoveIf()を、複雑なフィルタリングにはストリームを使用します:users = users.stream().filter(User::isActive).collect(Collectors.toList()).これらの手法は一括処理向けに最適化されており、ループで1つずつアイテムを削除するよりも効率的です。

今すぐ安全を確保しましょう

コード、クラウド、ランタイムを1つの中央システムでセキュアに。
脆弱性を迅速に発見し、自動的に修正。

クレジットカードは不要。