探索中にArrayListのエラーが報告される理由を分析する。
以前は、for(:){} を使用して List コレクションを反復処理し、同時に List コレクションの内容を変更すると ConcurrentModificationException エラーが報告されました。これは、メソッド内でオブジェクトが同時に変更されることを示すヒントですが、この例外はそのような変更が許可されていないときにスローされます。
1. コレクションをトラバースしながら同時修正をシミュレートする (a)
新しいリストコレクションを作成し、1~5の文字を順番にコレクションに追加し、コレクションをトラバースしながら、"2"を削除してください。
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
System.out.println("original list:" + list);
for (String string : list) {
System.out.println(string);
//If the fetched content is "2", delete it
if ("2".equals(string)) {
list.remove(string);
}
}
System.out.println("modified list:: " + list);
}
と出力されます。
The original list:[1, 2, 3, 4, 5]
1
2
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at com.test.jh.TestListDemo.main(TestListDemo.java:16)
コレクションを要素 "2" まで走査すると ConcurrentModificationException が発生しますが、どのメソッドが例外をスローするのでしょうか。実は、例外をスローするのは Iterator クラスの next() メソッドなのです。
2. コレクションのforeach探索の原理の解析
Decompile the code in the above mian method by decompiling technique
public static void main(String args[])
{
List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
//The underlying implementation of string concatenation by + is also through the StringBuilder class
System.out.println((new StringBuilder("original list:")).append(list).toString());
//2. The underlying use of for (String string : list) to traverse a collection is implemented using Iterators
for (Iterator iterator = list.iterator(); iterator.hasNext();)
{
//3. focus: here called next() method, in fact, the above error is called this method reported an error
String string = (String)iterator.next();
System.out.println(string);
if ("2".equals(string))
list.remove(string);
}
System.out.println((new StringBuilder("modified list:: ")).append(list).toString());
}
foreachがどのようにコレクションを走査するかを分析した後、基礎となるコレクションをIteratorを使って実装し、String string = (String)iterator.next(); という行を実行したためにエラーが報告されました。
3. ArrayListのソースコード解析
Iterator の next() メソッドをチェックアウトします。
行の実行によりエラーが発生したため。String string = (String)iterator.next(); という行の実行が原因なので、Iterator クラスのコードを見てみましょう。
// Assign the number of modifications to the collection (modCount) to expectedModCount and record it
int expectedModCount = modCount;
public E next() {
//It is mainly used to determine if the number of modifications to the collection is legal
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];
}
// Mainly used to determine if the number of modifications to a collection is legal
final void checkForComodification() {
//modCount represents the number of times the collection has been modified (e.g., add 1 for every list.add() and add 1 for every list.remove())
// the value of expectedModCount is equal to the number of times the collection was modified at the beginning of the iteration
if (modCount ! = expectedModCount)
throw new ConcurrentModificationException();
}
checkForComodification()メソッドは、modCountとexpectedModCountの値が等しくない場合に例外を発生させます。では、この2つの値はどこで修正されるのでしょうか?実は、modCountの値はリストコレクションがaddやremoveなどを実行したときに、expectedModCountの値はIterator()の新規作成時にexpectedModCountレコードに代入されるのです。
ArrayList クラスのソースコードを表示する
次に、ArrayList クラスの remove() コードを見て、remove メソッドが本当に modCount++ をオンにしていることを確認します。
public E remove(int index) {
rangeCheck(index);
// recorded the number of times the collection is modified, in fact, the collection of add () method also has modCount++; code
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
賢い人なら、上記のコードから、なぜ、コレクションを反復してその内容を変更し、checkForComodification()の値が不等間隔になったときにConcurrentModificationExceptionが投げられるかがわかると思います。
4. Listコレクションを反復処理し、同時にコレクションの内容を変更するとエラーが発生する原因
public static void main(String args[])
{
List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
System.out.println((new StringBuilder("original list:")).append(list).toString());
//3. Focus: list.iterator()
for (Iterator iterator = list.iterator(); iterator.hasNext();)
{
String string = (String)iterator.next();
System.out.println(string);
if ("2".equals(string))
list.remove(string);
}
System.out.println((new StringBuilder("modified list:: ")).append(list).toString());
}
1. コードがトラバーサル中に list.iterator() を実行すると、Iterator オブジェクトを作成するときに、modCount の値を expectedModCount に代入します。その時点で expectedModCount の値はコレクションが現在変更された回数となります。
//Executing list.iterator() calls this method
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator
{
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
// The value of modCount was assigned to expectedModCount when the Iterator object was created, and the value of expectedModCount is the current number of modifications to the set
int expectedModCount = modCount;
public boolean hasNext() {
return cursor ! = size;
}
.....
}
2. list.remove()を実行する際、以下の文に差し掛かると、modCount++;によってmodCountがexpectedModCountと不等号になります。次にString string = (String)iterator.next(); を呼び出すと、next() メソッドの checkForComodification() メソッドの modCount と expectedModCount の値が等しくないと例外がスローされることになります。ConcurrentModificationExceptionです。
if ("2".equals(string))
list.remove(string);
-
概要
実際には、例外として、コレクションをトラバースしている間にコレクションの内容を変更したため、modCountの値が増加し、expectedModCountの値が増加しなかったため、next()メソッドのcheckForComodification()メソッドが呼び出されてコレクションの変更回数が合法かどうか判断しエラーが報告されます。
6. コレクションをトラバースしながら修正するシミュレーション (II)
新しいリストコレクションを作成し、1~5の文字を順番にコレクションに追加し、コレクションをトラバースしながら"4"(最後から1つ目)を削除してください。
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
System.out.println("original list:" + list);
for (String string : list) {
System.out.println(string);
//Key: is to delete "4", not "2", then will report an error? Actually, no.
if ("4".equals(string)) {
list.remove(string);
}
}
System.out.println("modified list:: " + list);
}
と出力されます。
The original list:[1, 2, 3, 4, 5]
1
2
3
4
modified list:: [1, 2, 3, 5]
上記からわかるように、クソ、エラーも報告せずに正常に削除された?どうなってるんだ?
原因の分析
1. 以下のコードを実行すると、list.remove()で要素が削除され、サイズも1小さくなる。
//current cursor=4,size=5
if ("4".equals(string)) {
list.remove(string);//removes a size-1 equals 4
}
2. そして、再びhasNext()を決定するためにforループが強化されたとき。
public boolean hasNext() {
//when traversing to "4", the current cursor=4, the current size=4
return cursor ! = size;
}
3. iterator.hasNext() for (Iterator iterator = list.iterator(); iterator.hasNext();) を実行すると、戻り値が false になってしまうのはなぜですか?現在のカーソル=4,サイズ=4で、"4"までトラバースすると、hasNext()が返す値がfalseとなり、forループが直接終了し、5ループ目(最後のループ)を逃し、結果としてString string = ( String)iterator.next(); を実行できず、エラーは報告されない。
4. 削除したものが "4" ではなく "3" の場合、現在のカーソル = 3、現在のサイズ = 4 となり、次に iterator.hasNext() を実行すると、返ってくる結果は true となり、String string = (String)iterator.Iterator となる。 next();が実行される、要素の削除後、modCountの値が増加している結果、期待されるModCountの値が増加していないため、チェックForComodification()の呼び出しnext()メソッドでコレクションの修正カウントが合法かどうかを判断するエラーが報告されるでしょう。
関連
-
スタイルが読み込まれず、ブラウザのコンソールでエラーが報告される。リソースはスタイルシートとして解釈されますが、MIMEタイプtext/htmlで転送されます。
-
myeclipseでコンパイルするとAntエラーが発生する javaの例外が発生しました。
-
JQuery DataTable 详解
-
javaでよく使われる英単語
-
スレッド "main" で例外発生 java.net.BindException: アドレスは既に使用中です。NET_Bind
-
HttpClientがGZIP形式でない場合の対処法
-
アクセス制限です。タイプ 'JPEGCodec' は API エラーではありません。
-
Java入門-未報告例外 java.io.IOException; キャッチするか、スローされるように宣言する必要があります。
-
JsonMappingException処理方法
-
javaでlong型からint型に変換する。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Java Exceptionが発生しました エラー解決
-
スレッド "main" で例外発生 java.lang.ArrayIndexOutOfBoundsException: 0 at One1.main(One1.java:3)
-
このラインで複数のマーカーを解決する方法
-
java.security.InvalidAlgorithmParameterException: TrustAnchors パラメータは空であってはなりません 解決策
-
javaで "Unhandled exception type ...... "を処理するには?
-
Javaコレクションで一般的なcheckForComodification()メソッドは何をするのですか?modCount と expectedModCount の役割?
-
プロキシステータスコード 503_HTTP ステータス 503 エラーコードとその解決策?
-
Maven の例外:アーティファクト xxxx:pom が見つかりませんでした。
-
Gradleプロジェクトを作成すると、Could not install Gradle distribution from 'https://services.gradle.org/distributions/gr...と表示される。
-
SHA1暗号化アルゴリズム(Java実装)