#プログラミング勉強日記
2020年11月15日
Javaでコードを書きテストを使用してあっているか確かめていたところ、java.util.ConcurrentModificationException
という例外が発生したのでConcurrentModificationExceptionとは何なのか、どうすればいいのかまとめる。
#ConcurrentModificationExceptionとは
ConcurrentModificationExceptionのJavadocには以下のように書いてある。
この例外は、オブジェクトの並行変更を検出したメソッドによって、そのような変更が許可されていない場合にスローされます。
基本的には、繰り返しているものが変更されたときに早めに失敗して例外を投げるフェイルファーストするために使用される。つまり、繰り返し処理を終了する前に例外が発生している。
for-each文はIteratorを使用しているが、あまり冗長ではないが、テストでIteratorを使用するようにいリファクタリングした場合はremove()
などの追加のメソッドにアクセスできる。removeメソッドがConcurrentModificationExceptionを引き起こさないため、繰り返しの処理中に呼び出しても大丈夫である。
自分のコードの場合はArrayListの扱い方に問題があった。ArrayListとIteratorは内部で「変更された回数」を保持している。ArrayListのiterator()メソッドで生成されるIteratorはforで回す際に使うnext()メソッドなどを起動した際に,変更された回数にずれがないかチェックしていて、ずれがあったためConcurrentModificationExceptionの例外が発生していた。
JavadocのArrayListに以下の記述があった。
このクラスの iterator および listIterator メソッドによって返されるイテレータは、フェイルファストです。イテレータの作成後に、イテレータ自体の remove または add メソッド以外の方法でリストが構造的に変更されると、イテレータは ConcurrentModificationException をスローします。このように、並行して変更が行われると、イテレータは、将来の予測できない時点において予測できない動作が発生する危険を回避するために、ただちにかつ手際よく例外をスローします。
通常、非同期の並行変更がある場合、確かな保証を行うことは不可能なので、イテレータのフェイルファストの動作を保証することはできません。フェイルファストイテレータは最善努力原則に基づき、ConcurrentModificationException をスローします。したがって、正確を期すためにこの例外に依存するプログラムを書くことは誤りです。「イテレータのフェイルファストの動作はバグを検出するためにのみ使用すべきです」。
#参考文献
クラス ConcurrentModificationException
JavaでConcurrentModificationExceptionを回避