java foreach不适用的一种情况
java.util.ConcurrentModificationException
遍历集合找到特定的元素并将其删除,两种实现:
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的内部类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();
}
也就是集合被修改的次数(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的解决方案?
- 上一篇: TP5与TP3.2的可以通用的方法
- 下一篇: 游戏手柄(JoyStick)编程学习笔记(2)