在使用Iterator來迭代遍歷List的時候如果修改該List對象,則會報java.util.ConcurrentModificationException異常,下面看一個例子演示:
1 package com.others; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; 5 import java.util.List; 6 import java.util.concurrent.CopyOnWriteArrayList; 7 8 public class ArrayListTest { 9 10 public static void main(String[] args) { 11 List<String> list = new ArrayList<String>(); 12 //CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(); 13 list.add("a"); 14 list.add("b"); 15 list.add("c"); 16 list.add("d"); 17 list.add("e"); 18 Iterator iterator = list.iterator(); 19 while(iterator.hasNext()){ 20 String str = (String) iterator.next(); 21 if(str.equals("c")){ 22 list.remove(str); 23 }else{ 24 System.out.println(str); 25 } 26 } 27 } 28 29 }
結果為:
a b Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819) at java.util.ArrayList$Itr.next(ArrayList.java:791) at com.others.ArrayListTest.main(ArrayListTest.java:20)
當調用list的iterator()方法的時候,返回的是一個Itr對象(實現了Iterator接口):
1 public Iterator<E> iterator() { 2 return new Itr(); 3 }
我們看一下Itr這個類:
1 private class Itr implements Iterator<E> { 2 int cursor; // index of next element to return 3 int lastRet = -1; // index of last element returned; -1 if no such 4 int expectedModCount = modCount; //剛創建迭代對象的時候List的modCount 5 6 public boolean hasNext() { 7 return cursor != size; 8 } 9 10 @SuppressWarnings("unchecked") 11 public E next() { 12 checkForComodification(); //每調用一次next()函數都會調用checkForComodification方法判斷一次 13 int i = cursor; 14 if (i >= size) 15 throw new NoSuchElementException(); 16 Object[] elementData = ArrayList.this.elementData; 17 if (i >= elementData.length) 18 throw new ConcurrentModificationException(); 19 cursor = i + 1; 20 return (E) elementData[lastRet = i]; 21 } 22 23 public void remove() { 24 if (lastRet < 0) 25 throw new IllegalStateException(); 26 checkForComodification(); 27 28 try { 29 ArrayList.this.remove(lastRet); 30 cursor = lastRet; 31 lastRet = -1; 32 expectedModCount = modCount; 33 } catch (IndexOutOfBoundsException ex) { 34 throw new ConcurrentModificationException(); 35 } 36 } 37 //此方法用來判斷創建迭代對象的時候List的modCount與現在List的modCount是否一樣,不一樣的話就報ConcurrentModificationException異常 38 final void checkForComodification() { 39 if (modCount != expectedModCount) 40 throw new ConcurrentModificationException(); 41 } 42 }
List對象有一個成員變量modCount,它代表該List對象被修改的次數,每對List對象修改一次,modCount都會加1.
Itr類里有一個成員變量expectedModCount,它的值為創建Itr對象的時候List的modCount值。用此變量來檢驗在迭代過程中List對象是否被修改了,如果被修改了則拋出java.util.ConcurrentModificationException異常。在每次調用Itr對象的next()方法的時候都會調用checkForComodification()方法進行一次檢驗,checkForComodification()方法中做的工作就是比較expectedModCount 和modCount的值是否相等,如果不相等,就認為還有其他對象正在對當前的List進行操作,那個就會拋出ConcurrentModificationException異常。
我們再來分析一下上面那個例子,當例子程序執行到22行的時候,將list對象里面的“c"刪除了,同時list對象的modCount值加1,但是Itr對象的expectedModCount沒有變,他們肯定是不相等了。等再一次執行next()方法的時候調用了checkForComodification()方法,這時候就拋出異常了。
我們再將上面那個例子稍微改動一下:將21行改為if(str.equals("d")){,即刪除”d"這個元素。運行結果如下:
a
b
c
這時卻沒有報異常了,但是“e"卻沒有輸出來,這是為什么呢?原因很簡單,我們看到Itr的hashNext()方法:
1 public boolean hasNext() { 2 return cursor != size; 3 }
它是通過Itr的對象的cursor與List對象的size值來判斷是否還有未迭代的對象,當遍歷完“d"的時候cursor=4,刪除”d"的時候,List對象的size就會減1,size首先為5,后來變為4,這時候cursor和size是相等的,hasNext()方法返回的是false,就認為遍歷結束了,所以刪除以后沒有進去執行next()方法了,就沒有拋出異常了,當然"e"也沒有輸出來。
為了避免這種異常,我們可以使用CopyOnWriteArrayList來代替ArrayList,CopyOnWriteArrayList支持並發訪問,所以同時進行迭代和修改是沒有問題的。