JAVAにてMapをIteratorで回していたのですが、デバッグでIteratorの中を確認しているとConcurrentModificationExceptionが発生しました。
ConcurrentModificationExceptionはこちらで確認してみるとオブジェクトの並列変更を行うとエラーが発生するそうです。
デバック中Eclipseの式でデータ中身を表示した瞬間に例外が投げられるのはそのせいかな……
Iteratorはfail fastのため処理中に別スレッドでCollectionを変更したらダメだそうです。
対策としては同期化(スレッドセーフ)を行えばよいと思います。
同期化方法としてCollections.synchronizedMapを使うだと思います。
ただ、Iteratorを使う場合、要素数が変動されるとConcurrentModificationExceptionが発生してしまう可能性があります。
Map<String, String> test = new HashMap<String, String>();
Iterator<Map.Entry<String, String>> testIterator = Collections.synchronizedMap(test).entrySet().iterator();
while(testIterator.hasNext()){
Map.Entry<String, String> testEntry = (Map.Entry<String, String>) testIterator.next();
String testId = (String)testEntry.getKey();
String testValue = (String)testEntry.getValue();
}
↑だとダメらしい(Oracle Collectionsより)
Map<String, String> test = Collections.synchronizedMap(new HashMap<String, String>());
Set<Entry<String, String>> testSet = test.entrySet();
synchronized(test) {
Iterator<Entry<String, String>> testIterator = testSet.iterator();
while(testIterator.hasNext()){
Map.Entry<String, String> testEntry = (Map.Entry<String, String>) testIterator.next();
String testId = (String)testEntry.getKey();
String testValue = (String)testEntry.getValue();
}
}
のほうがいいかも。
keySetはsynchronizedより前に書くみたいなのでentrySetも同様と思います。
synchronizedMapを行う場合、Setはsynchronizedより
今回はCollections.synchronizedMapを例にしましたが他にも
Collections.synchronizedList
Collections.synchronizedSet
もあるそうです。(Oracle Collectionsより)
ConcurrentHashMapを使えば同期化処理が高速になるそうですが、通常のMapとは少し異なるのでえいやぁ!で一括置換するには危険だと思いますし、万能というわけでもないようです。(Java の ConcurrentHashMap における同期化より)
同期化処理難しいですね。