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