異常記録——java.util.ConcurrentModificationException
先日のプロジェクト統合の時、サーバーで私が担当している機能をテストした時、Exceptionが出てきましたが、これまでローカルでテストした時にテストされていませんでした.異常情報:java.util.ConcurrentModificationException
ボスが一目で分かるようになったのは、リスト集合を巡る際に、ArrayListのremove(Object o)メソッドを使ったということです.次は、例外が発生したことをテストする方法です.
前者はjdk 5である.0新しく追加されたループ構造は、集合の下付きを考慮することなく、集合内の各要素を処理するために使用できます.フォーマットは次のとおりです.
for(vaiable : collection) { statement; }
変数を定義して、セット内の各要素を一時保存し、対応する文ブロックを実行します.Collectionは配列またはIterableインタフェースを実装したクラスオブジェクトでなければなりません.
後者はインタフェースIterableを使用して、このインタフェースがオブジェクトを「foreach」文のターゲットにすることを可能にする.メソッドiterator()は、Tタイプの要素のセット上で反復する反復器を返します.
どちらもremove(Object o)メソッドを使用する場合に異常javaが発生する.util.ConcurrentModificationException.後者の分析によれば、分かりやすい.
異常が発生した場所:
iterator()メソッドは、ArrayListの親AbstractListで実装されていることがわかります.
非確定的な行為ではなく、迅速な失敗行為.
以上がAPI中国語翻訳、すなわち異常発生の原因である.
次に、例外が発生するプロセスを示します.
list.iterator()/Itr()クラスオブジェクトを作成し、データを初期化
cursor = 0;//カーソル
lastRet = -1;//前のインデックスの場所
expectedModCount = 0;//反復器は、後のリストにmodCountの値があるべきだと考えています.この予想が破壊されると、反復器は同時修正を検出します.
iterator.hasNext();//カーソル位置が集合サイズに等しいかどうかを判断し、falseを返す
iterator.next();//次へ移動
checkForComodification()を実行します.
list.remove(Object o);
この方法の実装:
次の実行
next()メソッドの場合、checkForComodification()の判断:
foreachループは実際にIteratorによって実現されるため、foreachループを用いて集合を巡る原理は同じである.同様にnext()メソッドを実行して検証を行い、同じ異常が発生します.
解決:iteratorを使用します.remove()メソッド
Itr()内部メソッド
ここではexpectedModCount=modCountを実行する.異常発生の防止
foreachループを使用してループし、削除する場合は、リストセットを再作成してマウントできます.
*注意:上記のテストはすべて単一スレッド環境で行われています.
ボスが一目で分かるようになったのは、リスト集合を巡る際に、ArrayListのremove(Object o)メソッドを使ったということです.次は、例外が発生したことをテストする方法です.
package com.day0219.list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author xukai
* List , remove(Object o)
*/
public class TestList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//
for(int i = 1; i < 100; i++){
list.add("str_" + i);
}
// foreach List, remove
for(String str : list){
// remove
if(Integer.parseInt(str.substring(4)) % 2 == 0){
list.remove(str);
}
}
// Iterator List
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String str = iterator.next();
if(Integer.parseInt(str.substring(4)) % 2 == 0){
list.remove(str);
}
}
// List
for(String str : list){
System.out.println(list.indexOf(str) + "," + str);
}
}
}
ここでは、2つのループ集合の方法が使用されている.前者はjdk 5である.0新しく追加されたループ構造は、集合の下付きを考慮することなく、集合内の各要素を処理するために使用できます.フォーマットは次のとおりです.
for(vaiable : collection) { statement; }
変数を定義して、セット内の各要素を一時保存し、対応する文ブロックを実行します.Collectionは配列またはIterableインタフェースを実装したクラスオブジェクトでなければなりません.
後者はインタフェースIterableを使用して、このインタフェースがオブジェクトを「foreach」文のターゲットにすることを可能にする.メソッドiterator()は、Tタイプの要素のセット上で反復する反復器を返します.
どちらもremove(Object o)メソッドを使用する場合に異常javaが発生する.util.ConcurrentModificationException.後者の分析によれば、分かりやすい.
異常が発生した場所:
iterator()メソッドは、ArrayListの親AbstractListで実装されていることがわかります.
public Iterator<E> iterator() {
return new Itr();
}
内部クラス: private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
フィールドの説明: protected transient int modCount = 0;
このリストを構造から変更した回数.構造的に変更とは、リストのサイズを変更したり、リストを乱したりして、進行中の反復にエラーの結果を生じさせることです.このフィールドはiterator
およびlistIterator
メソッドが返す反復器およびリスト反復器の使用.このフィールドの値を誤って変更した場合、反復器(またはリスト反復器)は放出されます.ConcurrentModificationException
応答next
、 remove
、 previous
、 set
またはadd
操作.反復中に同時変更に直面した場合、非確定的な行為ではなく、迅速な失敗行為.
以上がAPI中国語翻訳、すなわち異常発生の原因である.
次に、例外が発生するプロセスを示します.
list.iterator()/Itr()クラスオブジェクトを作成し、データを初期化
cursor = 0;//カーソル
lastRet = -1;//前のインデックスの場所
expectedModCount = 0;//反復器は、後のリストにmodCountの値があるべきだと考えています.この予想が破壊されると、反復器は同時修正を検出します.
iterator.hasNext();//カーソル位置が集合サイズに等しいかどうかを判断し、falseを返す
iterator.next();//次へ移動
checkForComodification()を実行します.
list.remove(Object o);
この方法の実装:
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
は実際にfastRemove(index)として実行される. private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
はまずmodCount++を見ることができて、値は変化して、最後に削除した要素のメモリアドレスをNULLに設定して、GCの回収を便利にします.次の実行
next()メソッドの場合、checkForComodification()の判断:
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
異常放出.foreachループは実際にIteratorによって実現されるため、foreachループを用いて集合を巡る原理は同じである.同様にnext()メソッドを実行して検証を行い、同じ異常が発生します.
解決:iteratorを使用します.remove()メソッド
Itr()内部メソッド
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
ここではexpectedModCount=modCountを実行する.異常発生の防止
while(iterator.hasNext()){
String str = iterator.next();
if(Integer.parseInt(str.substring(4)) % 2 == 0){
iterator.remove(); //
}
}
foreachループを使用してループし、削除する場合は、リストセットを再作成してマウントできます.
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//
for(int i = 1; i < 100; i++){
list.add("str_" + i);
}
List<String> temp = new ArrayList<>();
// foreach List, remove
for(String str : list){
// remove
if(Integer.parseInt(str.substring(4)) % 2 != 0){
temp.add(str);
}
}
list = null;// GC
// List
for(String str : temp){
System.out.println(temp.indexOf(str) + "," + str);
}
}
*注意:上記のテストはすべて単一スレッド環境で行われています.