Java8的官方文档中,对于iterator的forEachRemaining的用法介绍如下:
default void forEachRemaining(Consumer<? super E> action)
Performs the given action for each remaining element until all elements have been processed or the action throws an exception. Actions are performed in the order of iteration, if that order is specified. Exceptions thrown by the action are relayed to the caller.
Implementation Requirements:
The default implementation behaves as if:
while (hasNext())
action.accept(next());
详见:https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html
如果按照文档上的理解似乎可以在forEachRemaining的过程中remove元素:
ArrayList<String> list = new ArrayList();
for (int i = 0; i < 10; i++) {
list.add(String.valueOf(i));
}
Iterator iterator = list.iterator();
iterator.forEachRemaining(new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
if (o.equals("3") ) {
System.out.println("remove");
iterator.remove();
}
}
});
但是,却会报异常java.lang.IllegalStateException,和文档描述不一样。其实是因为在ArrayList中,接口Iterator的forEachRemaining方法被覆盖时,和接口文档描述不一致导致的。
private class Itr implements Iterator<E> { //arraylist的实现
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
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();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1; //只有所有acconsumer都执行完时,才会调整lastRest的值
checkForComodification();
}
}
原因如下:
①在ArrayList内的Iterator中,remove基于lastRet。
②next()后,lastRest属性会赋值为刚刚获取的元素的index。
③在remove后,lastRet会被置为-1,而且remove(),会校验lastRet值是否>=0,所以导致了一次next(),只能remove一次。
④而调用forEachRemaining就会遇到问题了,它在循环执行consumer的过程中,并非调用next(),而是手动的去向后遍历,在这个过程中并未将lastRet置为刚刚获取对象的index。只有遍历到最后一个以后,才会修改lastRet值。
所以一旦在cousumer中调用remove(),就有可能遇到lastRe为-1的情况,触发java.lang.IllegalStateException。