如果列表在任何时间从结构上修改创建迭代器之后,以任何方式除非通过迭代器自身remove/add方法,迭代器都将抛出一个ConcurrentModificationException,这就是单线程状态下产生的 fail-fast 机制。
fail-fast 机制:即快速失败机制,是java集合(Collection)中的一种错误检测机制。当在迭代集合的过程中该集合在结构上发生改变的时候,就有可能会发生fail-fast,即抛出 ConcurrentModificationException异常。fail-fast机制并不保证在不同步的修改下一定会抛出异常,它只是尽最大努力去抛出,所以这种机制一般仅用于检测bug。
Java8开始,可以使用Collection#removeIf()方法删除满足特定条件的元素,如
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 10; ++i) {
list.add(i);
}
list.removeIf(filter -> filter % 2 == 0); /* 删除list中的所有偶数 */
System.out.println(list); /* [1, 3, 5, 7, 9] */Copy to clipboardErrorCopied
java.util包下面的所有的集合类都是fail-fast的,而java.util.concurrent包下面的所有的类都是fail-safe的。
第一种不会抛出异常,而第二种方式会抛出ConcurrentModificationException异常。
对第二种代码进行反编译,得出的是
void testFastFail() {
List list = new ArrayList();
list.add("1");
list.add("2");
Iterator var2 = list.iterator();
while(var2.hasNext()) {
String s = (String)var2.next();
if (s.equals("2")) {
list.remove(s);
}
}
打入断点,查看其关键方法
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
cursor为当前指向的元素。size为当前列表元素个数。expectedModCount为记录修改次数。即先用hasNext()判断是否还有下一个元素,有就执行next(),然后判断modCount 是否等于expectedModCount,等于就继续执行,对if语句进行判断,执行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++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
这里会执行fastRemove方法,此时size--,modCount++,由于没有重新校正expectedModCount,故在checkForComodification方法中会抛出异常。
而iterator的remove方法
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();
}
}
会重新矫正expectedModCount的值,故不会报错。