9

ConcurrentModificationException とそれを回避する方法について読んでいました。記事を見つけました。その記事の最初のリストには、次のようなコードがあり、明らかに例外が発生する可能性があります。

List<String> myList = new ArrayList<String>();
myList.add("January");
myList.add("February");
myList.add("March");

Iterator<String> it = myList.iterator();
while(it.hasNext())
{
    String item = it.next();
    if("February".equals(item))
    {
        myList.remove(item);
    }
}

for (String item : myList)
{
    System.out.println(item);
}

その後、問題の解決方法をさまざまな提案とともに説明しました。

再現しようとしたところ、例外が発生しませんでした。例外が発生しないのはなぜですか?

4

4 に答える 4

8

Java API docs Iterator.hasNextによると、ConcurrentModificationException.

チェック"January"した後"February"、リストから 1 つの要素を削除します。呼び出しit.hasNext()は a をスローしませんが、ConcurrentModificationExceptionfalse を返します。したがって、コードはきれいに終了します。ただし、最後の文字列はチェックされません。リストに追加"April"すると、期待どおりに例外が発生します。

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

public class Main {
        public static void main(String args[]) {

                List<String> myList = new ArrayList<String>();
                myList.add("January");
                myList.add("February");
                myList.add("March");
                myList.add("April");

                Iterator<String> it = myList.iterator();
                while(it.hasNext())
                {
                    String item = it.next();
                    System.out.println("Checking: " + item);
                    if("February".equals(item))
                    {
                        myList.remove(item);
                    }
                }

                for (String item : myList)
                {
                    System.out.println(item);
                }

        }
}

http://ideone.com/VKhHWN

于 2013-02-03T14:52:55.203 に答える
4

ソースからArrayList(JDK 1.7):

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

で変更操作を行うたびにArrayList、フィールドがインクリメントされmodCountます (リストが作成されてから変更された回数)。

イテレータが作成されると、現在の値が に格納modCountされexpectedModCountます。ロジックは次のとおりです。

  • 反復中にリストがまったく変更されない場合、modCount == expectedModCount
  • リストが反復子自身のremove()メソッドによって変更された場合、インクリメントされますmodCountexpectedModCount、同様にインクリメントされるため、modCount == expectedModCount引き続き保持されます
  • 他のメソッド (または他の反復子インスタンス) がリストを変更すると、modCountインクリメントされるためmodCount != expectedModCount、結果は次のようになります。ConcurrentModificationException

ただし、ソースからわかるように、チェックはhasNext()メソッドでは実行されず、 でのみ実行されnext()ます。このhasNext()メソッドは、現在のインデックスとリスト サイズのみを比較します。最後から 2 番目の要素をリスト ( ) から削除する"February"と、次の の呼び出しがhasNext()単純に返さfalseれ、CME がスローされる前に反復が終了しました。

ただし、最後から 2 番目以外の要素を削除すると、例外がスローされます。

于 2013-02-03T15:39:06.027 に答える
1

正しい説明は、ConcurrentModificationExcetion の javadoc からの抜粋だと思います。

一般的に言えば、同期されていない同時変更が存在する場合に厳密な保証を行うことは不可能であるため、フェイルファストの動作は保証できないことに注意してください。フェイルファスト操作は、ベスト エフォート ベースで ConcurrentModificationException をスローします。したがって、この例外の正確性に依存するプログラムを作成するのは誤りです。ConcurrentModificationException は、バグを検出するためだけに使用する必要があります。

したがって、イテレータがフェイル ファストの場合、例外がスローされる可能性がありますが、保証はありません。あなたの例で置き換えFebruaryてみるJanuaryと、例外がスローされます(少なくとも私の環境では)

于 2013-02-03T14:53:16.747 に答える
0

反復子は、同時変更をチェックする前に、最後に到達したことを確認するために、残っている要素の数だけ反復したことをチェックします。これは、最後から 2 番目の要素だけを削除すると、同じ反復子に CME が表示されないことを意味します。

于 2013-02-03T20:37:37.000 に答える