代碼塊
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.mashibing.tank.Test2.main(Test2.java:14)
ArrayList在迭代刪除元素時,如果使用不當會發生concurrentModification 異常,常見的錯誤使用場景
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
Integer next = iterator.next();
list.remove(next);
}
for (Integer next:list
) {
list.remove(next);
}
發生異常原因:
首先明確以上兩種方式是等效的,原因:增強for在反編譯後語句
Iterator var4 = var1.iterator();
while(var4.hasNext()) {
Integer var3 = (Integer)var4.next();
var1.remove(var3);
}
然後我們去分析發生concurrentModification原因:
由於異常發生在 :java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909),我們先去看下此處代碼
發現remove(intxOrObject) 方法爲 ArrayList 內部類Itr的一個方法 ,當modCount != expectedModCount 時會拋出異常ConcurrentModificationException。
先來解釋下概念:
modCount:爲 成員變量 list 被修改的次數 ,每次add ,remove都會+1 (The number of times this list has been )
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>{
//The number of times this list has been
protected transient int modCount = 0;
}
expectedModCount:爲listIterator 方法 局部變量,表示對ArrayList修改次數的期望值,它的初始值爲modCount
public Iterator<E> iterator() {
return listIterator();
}
public ListIterator<E> listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
final int offset = this.offset;
return new ListIterator<E>() {
int cursor = index;
int lastRet = -1;
//注意這裏
int expectedModCount = ArrayList.this.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;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
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];
}
}
這裏假設原始list 長度爲10(只做過新增,沒有做過刪除操作) 。
這時候最初 AbstractList.modCount =10 ;
Iterator.expectedModCount = modCount =10 ;
1.當第一次循環調用next時,無變動list ,所以expectedModCount = modCount =10 ,
2.當 arrayList.remove(indexOrObject)時,modCount++ 變爲11 ,expectedModCount 依然爲10 。
3.第二次調用next 時,Itr 會執行checkForComodification() ,也就是判斷expectedModCount、 modCount 是否一致,如果不一致則拋出ConcurrentModificationException 。
正確姿勢:
關注點:
1.增強for循環不要刪除元素
2.使用Iterator<Integer> iterator = list.iterator(); 方式時,先獲取元素,然後使用iterator.remove() 進行刪除
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext() ) {
System.out.println(iterator.next());
iterator.remove();
}
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
list.remove(i);
}
爲什麼Iterator<Integer> iterator = list.iterator(); 方式時 用iterator remove 不會出現問題?看源碼:
private class Itr implements Iterator<E> {
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
//關注這裏, Itr裏將modCount 賦值給expectedModCount
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
爲什麼普通循環不會有ConcurrentModificationException ,看源碼:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
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;
}
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
}
}