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ループでの反復処理が原因で ConcurrentModificationException。イテレータは、コレクションがイテレータの外部で変更されたことを検出し、即座に例外をスローします。最初の非アクティブユーザー以降のアクティブユーザーは処理されません。

✅ 準拠済み:

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
    }
}

これが重要である理由: 使用 iterator.remove() イテレーション中に項目を安全に削除します。イテレータは一貫した状態を維持し、残りの項目の処理を続行します。すべての非アクティブユーザーは例外なく正しく削除されます。

まとめ

イテレータを使用してください。 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つの中央システムでセキュアに。
脆弱性を迅速に発見し、自動的に修正。

クレジットカードは不要です | スキャン結果は32秒で表示されます。