在ArrayList中有個成員變量modCount,繼承于AbstractList。
這個成員變量記錄着集合的修改次數,也就每次add或者remove它的值都會加1。這到底有什麼用呢?
先看下面一段測試代碼:
package temp;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class demo {
public static void main(String[] args){
List<String> list = new ArrayList<String>();
//CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("a");
Iterator iterator = list.iterator();
while(iterator.hasNext()){
String str = (String) iterator.next();
list.remove(str);
}
}
}
在使用疊代器周遊集合的時候同時修改集合元素。因為ArrayList被設計成非同步的,是以理所當然會抛異常。但是該抛什麼異常才能說明該問題呢?
首先得了解ArrayList的疊代器
public Iterator<E> iterator() {
return new Itr();
}
在調用list.iterator()的時候傳回的是一個Itr對象,它是ArrayList中的一個内部類。
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();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
主要關注3個點:
1、expectedModCount的初值為modCount
2、hasNext的判斷條件為cursor!=size,就是目前疊代的位置不是數組的最大容量值就傳回true
3、next和remove操作之前都會先調用checkForComodification來檢查expectedModCount和modCount是否相等
如果沒checkForComodification去檢查expectedModCount與modCount相等,這個程式肯定會報ArrayIndexOutOfBoundsException
這樣的異常顯然不是應該出現的(這些運作時錯誤都是使用者的邏輯錯誤導緻的,我們的JDK那麼高端,不會出現使用錯誤,我們隻抛出使用者造成的錯誤,而這個錯誤是設計者應該考慮的),為了避免出現這樣的異常,定義了檢查。是以抛出ConcurrentModificationException異常更能說明問題。
将測試代碼改成如下:
package temp;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class demo {
public static void main(String[] args){
List<String> list = new ArrayList<String>();
//CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
Iterator iterator = list.iterator();
while(iterator.hasNext()){
String str = (String) iterator.next();
if(str.equals("d")){
list.remove(str);
}else{
System.out.println(str);
}
}
}
}
輸出卻是 a b c。
因為在删除 d 的時候cursor為4,size也變成了4。是以hasNext就傳回為true了,循環結束,進而後面的元素也不會輸出了。
又想,為什麼不把hasNext()的判斷改為cursor <=size()呢?但是我們還有可能 add()這樣的話就會導緻資料混亂,事實上線程安全本身就不允許讀的時候被修改。
這種問題在多線程情況下,操作同一集合很容易暴露,就算改成同步的Vector問題還是會存在,需要使CopyOnWriteArrayList。