为什么java不要在foreach循环里进行元素的remove/add操作
PHPz
PHPz 2017-04-18 10:54:42
0
8
1219


选自《阿里巴巴JAVA开发手册》

图1代码执行情况是:解释删除1这个元素不会报错,但是删除2这个元素报错了,这个情况如何解释?

PHPz
PHPz

学习是最好的投资!

全員に返信(8)
大家讲道理

エラーの原因は、報告されたエラー checkForComodification() から知ることができます。エラーを回避したい場合は、 modCount != ExpectedModCount として保持する必要があります。 false
list.remove(Object)fastRemove(int) メソッドを呼び出します。このとき、必ず modCount が変更されます。今度はエラーが発生します。
Iterator<String> iterator = list.iterator() ;このメソッドの実装は、内部クラス Itr を返します (このクラスは反復で使用されます)。 process ) ですが、この iterator.remove() ではエラーが発生しないのはなぜでしょうか。その理由は、このメソッドの実装が実際の ArrayList.this.remove より前に実行されるためです。 checkForComodfication はチェックして remove を実行し、expectedModCount = modCount にするため、エラーは発生しません。 checkForComodification() ,如果要避免错误需要保持 modCount != expectedModCount false
list.remove(Object)会去调用fastRemove(int)方法,这个时候必然会去修改 modCount ,这个时候就会出现错误。
Iterator<String> iterator = list.iterator() ;这个方法的实现就是返回一个内部类 Itr,(迭代的过程都是使用的这个类),但是为什么这个 iterator.remove() 不会出现错误了,原因在与这个方法的实现是在进行实际的 ArrayList.this.remove 之前进行的 checkForComodfication 检查,remove 之后又使 expectedModCount = modCount,所以不会出现错误。

Itr.remove

Itr.remove 実装

リーリー

何か間違っていたらご指摘ください @ChaCha哥 @puluyinyi🎜
いいねを押す +0
Ty80

シングルスレッドの場合、List を走査するときに要素を削除するときは、List の Remove メソッドではなく Iterator の Remove メソッドを使用する必要があります。そうしないと、ConcurrentModificationException が発生します。想像してみてください。教師がクラス全体の生徒の数を数えているときに、生徒がルールを守らずに出たり入ったりした場合、教師は間違いなく生徒の数を数えることはできません。

マルチスレッドの場合は、私のブログの 1 つを参照してください: http://xxgblog.com/2016/04/02...

いいねを押す +0
Peter_Zhu

まず第一に、これにはマルチスレッド操作が含まれます。Iterator はマルチスレッド操作をサポートしていません。List クラスは、変更の数を記録するために内部で modCount 変数を維持します
例: ArrayList ソース コード

リーリー

イテレーターが生成されるたびに、イテレーターは next() メソッドが呼び出されるたびに modCount を記録します。レコードは外部クラス List の modCount と比較され、一致しないことが判明した場合は、マルチスレッド編集例外がスローされます。

なぜこんなことをするのですか?私の理解では、走査されるコレクションのコンテンツと密接に結合されたイテレータを作成したということです。つまり、このイテレータに対応するコレクションのコンテンツが現在のコンテンツであることを意味します。バブルソートのとき、まだデータをコレクションに挿入しているスレッドがありますよね?したがって、Java はこの単純な処理メカニズムを使用して、トラバーサル中にコレクションが変更されるのを防ぎます。

なぜ「1」を削除するだけで済むのかというと、その理由はforeachのhasNext()メソッドとイテレータにあります。実はforeachの糖衣構文は

です。 リーリー

したがって、すべてのループは最初に hasNext() を実行します。そのため、ArrayList の hasNext() がどのように書かれているかを見てください:

リーリー

cursor はイテレータの位置をマークするために使用される変数です。この変数は 0 から始まり、next が呼び出されるたびに +1 演算を実行します。つまり、次のようになります。
コードで「1」、size=1、cursor を削除した後。 =1、この hasNext() が false を返すと、ループが終了するため、反復子は 2 番目の要素を見つけるために next を呼び出さないため、modCount を検出する方法がなく、マルチスレッド変更例外は発生しません
しかし、 "2" を削除すると、反復子は next を 2 回呼び出し、size=1、cursor=2、および hasNext() が true を返したので、反復子は愚かにも next() を再度呼び出し、これにより modCount が等しくなくなり、スローされました。マルチスレッド変更の例外。

セットに要素が 3 つある場合、「1」を削除すると例外がスローされることがわかりますが、「2」を削除しても問題ありません。その理由は、上記のプログラムの実行順序が一貫しているためです。

いいねを押す +0
黄舟

要素を追加または削除するとコレクション内の番号が変わるため、たとえば、コレクションに 10 個の要素がある場合、要素を追加するとき、または要素を削除するときに、その番号を 10 回走査する必要があります。トラバーサルの回数が正しくないため、エラーが報告されます

いいねを押す +0
PHPzhong

とにかく、リストを削除しないようにしてください。削除マークを追加できます

いいねを押す +0
伊谢尔伦

文書内の黄色の説明は非常に興味深いです。

この例の実行結果は誰もが予想を超えるものになるでしょう。それでは、「1」を「2」に置き換えてみてください。同じ結果になりますか?

まだ ArrayList のソースコードを見る必要がありますが、一目でそれがわかります。

いいねを押す +0
巴扎黑

逆の順序で削除してください

いいねを押す +0
Ty80

ArrayList はスレッドセーフではありません。つまり、トラバース中に List を変更することになります。この場合、ArrayList は同時変更例外をスローします。

いいねを押す +0
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート