ArrayList同時修正異常
35408 ワード
まず次のコードを見てください。
public static void main(String[] args) {
ArrayList<Students> arrayList = new ArrayList<Students>();
arrayList.add(new Students(" ", 23));
arrayList.add(new Students(" ", 24));
arrayList.add(new Students(" ", 24));
Iterator<Students> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Students next = iterator.next();
if (next.getSname().equals(" ")) {
arrayList.remove(next);
}
}
}
実行結果は次のとおりです。
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1009)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:963)
at com.czn.collections.ArrayList.IteratorDemo.main(IteratorDemo.java:15)
探究する
ArrayListの同時修正異常が発生しましたが、なぜこのような異常が発生したのでしょうか?ソースコードを見てみましょう.
(一)add(E e)方法
public boolean add(E e) {
// 3
// add 1, add ,
// modCount 3
modCount++;
add(e, elementData, size);
return true;
}
(二)arrayList.iterator()メソッド
Iterator iterator = arrayList.iterator();
public Iterator<E> iterator() {
// ArrayList Itr
return new Itr();
}
Itrクラスを見てみましょう(まず一部を切り取ります):この部分は主にItrクラスのメンバー変数の初期化です.
private class Itr implements Iterator<E> {
// : 0
int cursor;
// , -1
int lastRet = -1;
//
int expectedModCount = modCount;
//Itr
Itr() {}
ここで注意すべき点はexpectedModCountも3に割り当てられていることです
(三)iterator.hasNext()メソッド
// Itr
public boolean hasNext() {
return cursor != size;
}
カーソルがarrayListのsize(lengthではないことに注意)に等しくない場合は、まだ最後の位置に着いていないことを示し、trueを返します.そうしないと、最後の位置に着いたことを示し、次の位置がないことを示し、falseを返します.
(四)iterator.next()メソッド
@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];
}
ここでは主にcheckForComodification()メソッドに注意する
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
実際の修正回数と所望の修正回数が一致しない場合は、同時修正異常を投げ出すmodCountとexpectedModCountはいずれも3に等しいため、まだ異常を投げ出すことはありません
(四)arrayList.remove()メソッド
私たちのコードは3番目の要素に遍歴するとif文ブロックに入ります.
if (next.getSname().equals(" ")) {
arrayList.remove(next);
}
remove()メソッドのソースコードに入ります.
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
fastRemove(es, i);
return true;
}
// fastRemove()
private void fastRemove(Object[] es, int i) {
//
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
以上のソースコードから分かるようにarrayList.remove()メソッドはmodCountの自己増加(すなわちmodCount==4)も行い、このときarrayListのsizeは2になってwhileサイクル
iterator.hasNext()
に入る:public boolean hasNext() {
// cursor 3, size 2, true
return cursor != size;
}
次の行コード
Students next = iterator.next();
の実行を続行@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];
}
このメソッドcheckForComodification()のソースコードは、次のように強調されています.
final void checkForComodification() {
//modCount = 4 expectedModCount =3
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
ここでは同時修正異常を投げ出しました
特別な場合
ArrayList<Students> arrayList = new ArrayList<Students>();
arrayList.add(new Students(" ", 23));
//
arrayList.add(new Students(" ", 24));
arrayList.add(new Students(" ", 24));
Iterator<Students> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Students next = iterator.next();
if (next.getSname().equals(" ")) {
arrayList.remove(next);
}
この場合、2番目の要素が削除されると、削除後に
// while
public boolean hasNext() {
//cursor = 2 size = 2, false
return cursor != size;
}
したがってwhileループは終了し、要素は正常に削除されましたが、同時変更例外は発生しません.理解していない読者は、上記のソースコードに従って一度で理解できるはずですが、2つの状況を比較して理解すると、効果的ですよ.
解決策
ArrayList<Students> arrayList = new ArrayList<Students>();
arrayList.add(new Students(" ", 23));
arrayList.add(new Students(" ", 24));
arrayList.add(new Students(" ", 24));
Iterator<Students> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Students next = iterator.next();
if (next.getSname().equals(" ")) {
iterator.remove();
}
}
この同時修正異常の問題はiteratorのremove()メソッドを用いて解決できる
// Itr remove()
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
// ArrayList remove()
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
// ,
// ,
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
まとめ
1,集合がaddメソッドを呼び出すたびに,実際の修正回数変数の値は1回ずつ増加する2,反復器を取得するときは,集合は実際の修正集合の回数を所望の修正集合の回数に1回だけ付与する3.集合は、要素を削除する際にも、実際の修正回数の変数に対して自増操作を行う.削除する要素が最後でない場合、反復器の遍歴中にArrayList.remove(E e)メソッド削除要素に同時修正異常は発生しない.利用できるremove()の方法で同時修正異常の問題を解決する