foreachはlistを巡って要素を削除すると必ずエラーが表示されますか?
4911 ワード
foreachはlistコレクションを巡っていくつかの要素を削除すると必ずエラーが表示されますか?さあ、前のコードに進みます.
1)誤報
当然、コンソールはjavaを楽しく報告した.util.ConcurrentModificationException.
これはどういうことなのか、それからこの異常を見に行って、自分がやはり若すぎることに気づいた.
forループ、すなわちforeachループを追加することは、リストオブジェクトに基づいてiterator反復オブジェクトを作成することであることが知られています.この反復オブジェクトを使用してlistを遍歴することは、listオブジェクトの要素の遍歴がiteratorに管理されていることに相当します.listを削除するには、iteratorを経由する必要があります.
foreachサイクルのたびに、次の2つの操作があります.
1.iterator.hasNext();//次の要素があるかどうかを判断します.
2.item = iterator.next();//次の要素は何ですか.itemに割り当てます.
まず、この異常情報が何なのか見てみましょう.
checkForComodification()メソッドに入ったときにエラーが表示されます.つまりmodCount!=expectedModCount.具体的には,foreach方式で要素を遍歴する場合,iteratorを生成し,iterator遍歴を用いる.iteratorを生成するときにexpectedModCountパラメータが保存されます.これはiteratorを生成するときにリストで要素を変更した回数です.遍歴中に要素を削除すると、ListではmodCountが変化し、このmodCountとexceptedModCountが一致しないと異常が放出されます.これは安全のためです.リストのremoveソースを見てください.
見て、expectedModCountに対していかなる修正を行っていないで、expectedModCountとmodCountが一致しないことを招いて、異常を投げ出します.したがって、list削除要素を巡回するには、次のようにIteratorを使用します.
Iteratorのremove()メソッドのソースコードを見てみましょう.expectedModCountには、次のように割り当てられています.
これでexpectedModCount=modCountを等しくしておけばエラーは発生しません.
2)foreachのすべてのリスト削除操作でこのエラーが報告されるのではないでしょうか
削除された要素が最後から2番目の数であれば、実は間違いを報告しないことに気づいたかどうか、なぜか、一緒に見てみましょう.
前にforeachループは2つの方法hasNext()とnext()を歩きますと言いました.間違いを報告したくなければnext()メソッドに入らなければ良いのでhasNext()のメソッドを見てみましょう.
ではhasNext()のメソッドにfalseを返すように要求します.すなわちcursor==sizeです.ここでcursorは、現在のiteratorの位置情報を0から保存するためのItrクラス(Iteratorサブクラス)のフィールドです.cursor自体がカーソルという意味で、データベースの操作で使われることが多い.curosrがsizeに等しくない限り要素が存在すると考えられる.ItrはArrayListの内部クラスであるため、直接ArrayListのsizeフィールドが呼び出されるので、このフィールドの値は動的に変化しており、動的に変化している以上問題が発生する可能性がある.
上記のコードを例にとると、最後から2番目のデータである「4」までの間、cursorは4であり、削除操作が呼び出されるとsizeは5から4になり、hasNext判定が呼び出されると、cursor=sizeとなり、後の操作が呼び出されてそのままループを終了する.上のコードに1行のコードを追加して効果を表示できます.
出力は:1 2 3 4
これによりhasNext()メソッドを実行して終了し,後に進む異常も見られなくなる.
これにより,foreachでlist要素を削除した場合,最後から2番目の要素だけを削除してもエラーは報告されず,他はエラーが報告されるのでIteratorを使用したことが分かる.
never too late! 参照先:http://rongmayisheng.com/post/%E7%A0%B4%E9%99%A4%E8%BF%B7%E4%BF%A1java-util-arraylist%E5%9C%A8foreach%E5%BE%AA%E7%8E%AF%E9%81%8D%E5%8E%86%E6%97%B6%E5%8F%AF%E4%BB%A5%E5%88%A0%E9%99%A4%E5%85%83%E7%B4%A0
http://blog.csdn.net/Jywangkeep_/article/details/48754189
1)誤報
List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
for (String item : list) {
if (item.equals("3")) {
System.out.println(item);
list.remove(item);
}
}
System.out.println(list.size());
当然、コンソールはjavaを楽しく報告した.util.ConcurrentModificationException.
これはどういうことなのか、それからこの異常を見に行って、自分がやはり若すぎることに気づいた.
forループ、すなわちforeachループを追加することは、リストオブジェクトに基づいてiterator反復オブジェクトを作成することであることが知られています.この反復オブジェクトを使用してlistを遍歴することは、listオブジェクトの要素の遍歴がiteratorに管理されていることに相当します.listを削除するには、iteratorを経由する必要があります.
foreachサイクルのたびに、次の2つの操作があります.
1.iterator.hasNext();//次の要素があるかどうかを判断します.
2.item = iterator.next();//次の要素は何ですか.itemに割り当てます.
まず、この異常情報が何なのか見てみましょう.
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
checkForComodification()メソッドに入ったときにエラーが表示されます.つまりmodCount!=expectedModCount.具体的には,foreach方式で要素を遍歴する場合,iteratorを生成し,iterator遍歴を用いる.iteratorを生成するときにexpectedModCountパラメータが保存されます.これはiteratorを生成するときにリストで要素を変更した回数です.遍歴中に要素を削除すると、ListではmodCountが変化し、このmodCountとexceptedModCountが一致しないと異常が放出されます.これは安全のためです.リストのremoveソースを見てください.
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;
}
見て、expectedModCountに対していかなる修正を行っていないで、expectedModCountとmodCountが一致しないことを招いて、異常を投げ出します.したがって、list削除要素を巡回するには、次のようにIteratorを使用します.
Iterator it = list.iterator();
while(it.hasNext()){
if(it.next().equals("3")){
it.remove();
}
}
Iteratorのremove()メソッドのソースコードを見てみましょう.expectedModCountには、次のように割り当てられています.
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;// expectedModCount
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
これでexpectedModCount=modCountを等しくしておけばエラーは発生しません.
2)foreachのすべてのリスト削除操作でこのエラーが報告されるのではないでしょうか
削除された要素が最後から2番目の数であれば、実は間違いを報告しないことに気づいたかどうか、なぜか、一緒に見てみましょう.
前にforeachループは2つの方法hasNext()とnext()を歩きますと言いました.間違いを報告したくなければnext()メソッドに入らなければ良いのでhasNext()のメソッドを見てみましょう.
public boolean hasNext() {
return cursor != size;
}
ではhasNext()のメソッドにfalseを返すように要求します.すなわちcursor==sizeです.ここでcursorは、現在のiteratorの位置情報を0から保存するためのItrクラス(Iteratorサブクラス)のフィールドです.cursor自体がカーソルという意味で、データベースの操作で使われることが多い.curosrがsizeに等しくない限り要素が存在すると考えられる.ItrはArrayListの内部クラスであるため、直接ArrayListのsizeフィールドが呼び出されるので、このフィールドの値は動的に変化しており、動的に変化している以上問題が発生する可能性がある.
上記のコードを例にとると、最後から2番目のデータである「4」までの間、cursorは4であり、削除操作が呼び出されるとsizeは5から4になり、hasNext判定が呼び出されると、cursor=sizeとなり、後の操作が呼び出されてそのままループを終了する.上のコードに1行のコードを追加して効果を表示できます.
for (String item : list) {
System.out.println(item);
if (item.equals("4")) {
list.remove(item);
}
}
出力は:1 2 3 4
これによりhasNext()メソッドを実行して終了し,後に進む異常も見られなくなる.
これにより,foreachでlist要素を削除した場合,最後から2番目の要素だけを削除してもエラーは報告されず,他はエラーが報告されるのでIteratorを使用したことが分かる.
never too late! 参照先:http://rongmayisheng.com/post/%E7%A0%B4%E9%99%A4%E8%BF%B7%E4%BF%A1java-util-arraylist%E5%9C%A8foreach%E5%BE%AA%E7%8E%AF%E9%81%8D%E5%8E%86%E6%97%B6%E5%8F%AF%E4%BB%A5%E5%88%A0%E9%99%A4%E5%85%83%E7%B4%A0
http://blog.csdn.net/Jywangkeep_/article/details/48754189