遍历集合找到特定的元素并将其删除,两种实现:
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方法源代码:
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的内部类Iterator的一个实例。
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();
}
也就是集合被修改的次数(modCount)和它的期望值(expectedModCount)不同,那么就会抛出ConcurrentModificationException异常。
再来看看Iterator的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时也会出现这种情况,但是在用普通的for循环却不会。
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的接口,该接口保护了一个能够产生Iterator的iterator()方法,并且Iterable接口被 foreach用来在序列中移动。”(<<Thinking in java>>)
网上其他解释:
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 同意这种解释,从代码出发,比较好理解。
http://gceclub.sun.com.cn/yuanchuang/week-14/iterator.html
嗯,是否有还使用foreach的解决方案?