为什么使用迭代器时可以用集合对象删除元素?
我觉得题主的首要问题是被自己写的代码(以及JDK的ArrayList那可恶的API)给坑了。
ArrayList上有两个版本的remove方法:
public E remove(int index)
public boolean remove(Object o)
题主很可能以为自己调用的是第二个版本,但实际上调用的是第一个版本——remove(3) 删除了位于末尾的元素,而不是位于倒数第二的元素。
ArrayList.iterator() 返回出来的 Iterator<E>,里面的hasNext()是不关心modification count的,而next()会去检查modification count:
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;
Itr() {}
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];
}
// ...
}
所以题主的那个程序实际做的事情就是:
- 通过 ArrayList.iterator() 得到了一个新的iterator,开始遍历
- list.remove(3):删除了位于index 3的元素(Integer.valueOf(4) 得到的对象)
- 然后调用iterator.hasNext(),得到false,于是就退出了循环而没有去执行那个会检查modification count的next()方法。
ArrayList的JavaDoc说:
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
没有特别指定Iterator里的哪些方法一定会根据fail-fast原则而抛异常。但Iterator.hasNext()的JavaDoc说:
hasNextReturns true if the iteration has more elements. (In other words, returns true if next() would return an element rather than throwing an exception.)boolean hasNext()
Returns:true if the iteration has more elements
就这个规定来说,我觉得题主观察到的现象应该算是JDK实现的上的巧合:因为如果在这个位置调用next()的话会抛ConcurrentModificationException异常,所以hasNext()也要返回false,于是就好啦。
但JDK这个具体实现看起来还是有bug,应该让hasNext()也做checkForComodification()的不抛异常对应动作才对。
就这样。