Java8,iterator的forEachRemaining中remove可能抛异常

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。





  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值