單執行緒的Iterator迭代過程中刪除集合元素
public class TestIterator {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String next = it.next();
if("3".equals(next)) {
list.remove(3);
}
System.out.println(next);
}
}
}
以上程式碼會出現如下異常
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at caoyan.test.TestIterator.main(TestIterator.java:22)
從後往前看:
TestIterator第22行程式碼:
String next = it.next();
我們在執行程式碼22行時呼叫了it.next();這個it是ArrayList呼叫iterator()返回的物件;這個物件的next()方法如下圖:
public E next() {
checkForComodification();
...
next()方法首先它會呼叫checkForComodification()這個方法,這個方法很簡單,就是比較expectedModCount , modCount 這兩個值是不是相等;不相等就丟擲異常;如下圖:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
這兩個值為什麼會不相等,它們各自代表什麼含義呢,我們看ArrayList的iterator()方法:
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
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;
Arraylist的iterator()方法,直接建立了一個私有的內部類Itr的物件;expectedModCount 就是定義在這個Itr中的成員變數,而且,在初始化的時候,將modCount賦值給expectedModCount,說明在剛生成Iterator的時候這兩個值是相等的.modCount是定義在哪裡呢,看下面原始碼
public class ArrayList<E> extends AbstractList<E>
...
protected transient int modCount = 0;
modCount是AbstractList的成員變數,ArrayList繼承下來了.所以modCount代表ArrayList的實際長度.
當我們在iterator的迭代過程中改變了ArrayList的長度,就導致modCount變了,而expectedModCount 沒有變,導致不相等報錯
可是為什麼用iterator的remove方法就不報錯呢,我們看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在刪除元素後,expectedModCount = modCount;重新賦值,讓這兩個值相等了.所以,remove方法不會丟擲這個異常