。遍歷集合找到特定的元素并將其刪除,兩種實現(xiàn):
private void testDelete() {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10; i++) {
String str = "ck0" + i;
list.add(str);
}
for (Iterator it = list.iterator(); it.hasNext();) {
String str = (String) it.next();
if (str.equals("ck05")) {
// list.remove(str); // 第一種刪除方法
it.remove(); // 第二種刪除方法
}
}
}
當通過list.remove(str)刪除時報異常:java.util.ConcurrentModificationException。而通過it.remove()刪除時一切正常。
先看看List中的remove方法:
這里用的ArrayList,ArrayList中remove方法源代碼:
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++; // 特別注意這里,這里只增加了modCount的值
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
}
到這里似乎還沒有找到拋出異常的地方,接著看。刪除后得到下一個元素的代碼,it.next(): it為AbstractList的內(nèi)部類Iterator的一個實例。
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
也就是集合被修改的次數(shù)(modCount)和它的期望值(expectedModCount)不同,那么就會拋出ConcurrentModificationException異常。
再來看看Iterator的remove()方法的源代碼:
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount; // 設置expectedModCount
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
到這里問題就很明白了!
在我們foreach時也會出現(xiàn)這種情況,但是在用普通的for循環(huán)卻不會。
for(String str : list){
if(str.equals("ck05")){
list.remove(str); // 報異常
}
}
for(int i = 0; i < list.size(); i++){
String str = list.get(i);
if(str.equals("ck05")){
list.remove(str); // 正常
}
}
“之所以可以這樣做(用foreach遍歷集合),是因為Java SE5引入了新的的被稱為Iterable的接口,該接口保護了一個能夠產(chǎn)生Iterator的iterator()方法,并且Iterable接口被 foreach用來在序列中移動?!?<<Thinking in java>>)
網(wǎng)上其他解釋:
http://www.javaeye.com/topic/124788
文中指出:“有意思的是如果你的 Collection / Map 對象實際只有一個元素的時候, ConcurrentModificationException 異常并不會被拋出。這也就是為什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.” 測試了下,當只要一個元素時仍然會報異常!
http://www.javaeye.com/topic/145383 同意這種解釋,從代碼出發(fā),比較好理解。
http://gceclub.sun.com.cn/yuanchuang/week-14/iterator.html