ルール
使用 安全な メソッド するときは 除去 コレクションから
変更 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() 反復処理中に直接
.avif)
