12 Dec 2014, 15:59

ConcurrentModificationException が Java で発生したときの対処方法

はじめに

ConcurrentModificationException が Java で発生したときの対処方法.

以下のようなコードを実行すると, 例外発生.

import java.util.Set;
import java.util.HashSet;

public class ConcurrentModification {
    public static void main (String[] args) {
        Set<Integer> set = new HashSet<Integer>();
        for (int i = 0; i < 5; i++)
            set.add (i);

        for (Integer i: set) {
            if (i == 3) {
                set.remove (i);
            }
        }
    }
}
Exception in thread "main" java.util.ConcurrentModificationException
   at java.util.HashMap$HashIterator.nextEntry (HashMap.java:922)
   at java.util.HashMap$KeyIterator.next (HashMap.java:956)
   at ConcurrentModification.main (ConcurrentModification.java:10)

原因は, iterater で for 文を回している時に, 要素を削除しようとしたから.

回避方法

その 1: イテレータを利用しない

イテレータなんてつかってかっこつけているのが悪い. Index で for 文をまわす

for (int i=0; i < set.size (); i++) {
    if (i == 3) {
        set.remove (i);
    }
}

その 2 Concurrent ライブラリを利用する

ConcurrentHashMap を利用する. ただし, ConcurrentHashSet はない…以下のように対応

Set<Integer> set = Collections.newSetFromMap (new ConcurrentHashMap<Integer, Boolean>());

その 3 コレクションをコピーして回す

すこし冗長か?

Set<Integer> set = new HashSet<Integer>();
for (int i = 0; i < 5; i++)
    set.add (i);

Set<Integer> set2 = new HashSet<Integer>();
set2.addAll (set);

for (Integer i: set2) {
    if (i == 3) {
        set.remove (i);
    }
}

おわりに

やりかたはいろいろある. 1 がいいかな…

BookMark