for (Order order:doneOrders) {
if (comment.getCommentOrderId().equals(doneOrders.get(i).getOrderId())){
doneOrders.remove(i);
}else {
i++;
}
}
以上代碼拋出了ConcurrentModificationExpection
為什么?
原因是在使用迭代器回圈遍歷ArrayList時, 在回圈中使用了ArrayList.remove() 移除元素,
@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) //expectedModCount是Itr的成員變數
throw new ConcurrentModificationException();
}
在ArrayList中有一個私有內部類Itr (文末有jdk1.8 的 Itr原始碼),它實作了Iterator介面,在迭代list時,會回圈執行next()方法,在next方法體中第一行,呼叫了checkForComodification方法,

ArrayList 類繼承了 AbstractList 類
在AbstractList 類中定義了一個成員變數 modCount (該list的尺寸被修改的次數) 初始值為0

//ArrayList 中的remove 方法
public E remove(int index) {
rangeCheck(index);
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;
}
由上面代碼可看出,ArrayList 在呼叫自己的remove 方法移除元素時,會將modCount +1
然而在迭代器回圈變數 arrayList 時,使用的next方法, next方法體的第一行,呼叫了checkForComodification() 方法,檢查modCount 與 expectedModCount 是否一致,如果不一致則拋出ConcurrentModificationExpection (在對一個集合物件進行迭代操作的同時,并不限制對集合物件的元素進行操作 這些操作包括一些可能引起跌代錯誤的add()或remove()等危險操作,以防后面還沒有被遍歷到的元素可能被修改?)
如何避免?
1、單執行緒情況下,在使用迭代器回圈變數時,可以使用ArrayList 私有內部類 Itr 的remove 方法,
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
它的核心代碼雖然還是ArrayList 的remove方法,會使modCount+1
但他在下面對 游標cursor ,lastRet ,exprctedModCount 進行了重新賦值,使得遍歷下個元素呼叫next方法并呼叫checkForComodification() 方法時,不會拋出例外,
2、多執行緒情況下,使用CopyOnWriteArrayList,替換ArrayList,
CopyOnWriteArrayList內部的迭代方法沒有 checkForComodification(), 但CopyOnWriteArrayList保證的是最終一致性,不能保證資料的實時一致性,
這是因為CopyOnWriteArrayList在寫時其實是將原陣列復制一份,在新陣列中修改,再把原陣列的呼叫指向新陣列,寫的程序是加鎖的,否則多個執行緒修改list會copy出多個版本,
在讀時,如果有多個執行緒在修改list,依舊只能讀到舊的原陣列,其他執行緒對新陣列的修改影響不到讀取,
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
以下為ArrayList 私有內部類 Itr 的原始碼
cursor是指集合遍歷程序中的即將遍歷的元素的索引,lastRet是cursor -1,默認為-1,即不存在上一個時,為-1,它主要用于記錄剛剛遍歷過的元素的索引,expectedModCount這個就是fail-fast(快速失敗)判斷的關鍵變數,它初始值就為ArrayList中的modCount,
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/237197.html
標籤:其他
上一篇:pikachu xss合集(下)
