8

一部のスレッドがイテレータを使用してコレクションをトラバースしている間にコレクションが変更されると、 iterator.next() がConcurrentModificationExceptionをスローすることを知っています。

ただし、リスト内の要素の数に応じて異なる動作を示します。

for-each ループでリストをトラバースし、そのトラバーサルの間に、リストの remove() メソッドを使用してリストから要素を削除するコード スニペットを試しました。

リストの要素数に依存せずに、この条件で ConcurrentModificationException をスローするのが理想的ですが、リストの要素数が 2 の場合はそうではありません。

ケース 1: リスト内の要素数 - 1

 public static void main(String[] args) 
    {
        List<String> list=new ArrayList<String>();
        list.add("One");

        for (String string : list) 
        {
            System.out.println(string);
            list.remove(string);
        }
    }

出力: 1

スレッド「メイン」での例外 java.util.ConcurrentModificationException

それは予想通りでした。

ケース 2:リスト内の要素数 - 2

 public static void main(String[] args) 
    {
        List<String> list=new ArrayList<String>();
        list.add("One");
        list.add("two");

        for (String string : list) 
        {
            System.out.println(string);
            list.remove(string);
        }
    }

出力: 1

例外はスローされません?????????

ケース 3:リスト内の要素数 - 3

 public static void main(String[] args) 
    {
        List<String> list=new ArrayList<String>();
        list.add("One");
        list.add("Two");
        list.add("Three");

        for (String string : list) 
        {
            System.out.println(string);
            list.remove(string);
        }
    }

出力: 1

スレッド「メイン」での例外 java.util.ConcurrentModificationException

再び例外がスローされますが、これは理想的な動作です。

しかし、 ConcurrentModificationException をスローせずにケース 2で適切に実行されるのはなぜですか。

4

3 に答える 3

7

投稿された以前の回答は、これが適切な動作である理由を説明する関連ドキュメントを示しています。その例外を受け取る保証はありません。

なぜ2 つの要素だけで受信しないのか (または、サイズに関係なく最後から 2 番目の要素を削除した場合) を知りたい場合は、 のソース コードを参照してくださいArrayList

あなたのループ:

for (String string : list)

実際には:

for(Iterator<String> i = list.iterator(); i.hasNext(); ) {
    String string = i.next();
    ...
}

内部的に Arraylist には、 内int sizeの現在の要素数を表す がありArrayListます。を呼び出すとデクリメントされますremove()

の内部Iteratorには、int cursorの現在の位置 (インデックス) を表す がありIteratorます。呼び出すと増加しますnext()

hasNext()サイズに対して現在のカーソル位置をIteratorチェックします。

あなたの例では、一連のイベントは次のとおりです。

  • cursorで開始、0size開始2
  • next()が呼び出され、 にcursorインクリメントされ1ます。
  • remove()呼び出され、sizeデクリメントされます1
  • hasNext()と比較cursorsize、それらが同じであることを発見し、返すfalse
  • ループは例外をスローせずに終了します

そのため、反復中に任意のサイズの最後の要素から 2 番目を削除してもArrayList、例外は発生しません。(また、ループ内のそのリストの最後の要素を処理しないことにも注意してください。例Oneはその理由でのみ印刷されます)。

ただし、これは実装の詳細であり、保証されていません。ドキュメントには、例外がスローされる (またはスローされない) ことに依存するべきではないと書かれています。ArrayList上記が適用されなくなり、例外スローされる別の方法で書き直すことができます (実際、別の JVM では、すでにそうなっている可能性があります)。

于 2013-07-21T13:28:50.057 に答える
0

指定された要素をリストから削除すると、リストはそのサイズが変更されたことを認識していないようです。

これを試して:

イテレータ.remove()

基になるコレクションから、反復子によって返された最後の要素を削除します (オプションの操作)。このメソッドは、next への呼び出しごとに 1 回だけ呼び出すことができます。反復の進行中に、このメソッドを呼び出す以外の方法で基になるコレクションが変更された場合、反復子の動作は規定されていません。

于 2013-07-21T12:24:17.307 に答える