Iterator remove 抛出异常 IllegalStateException 排查

Scroll Down

出现异常代码如下

for (Iterator<PromotionChannelShopPo> iterator = channelShopPoList.iterator(); iterator.hasNext(); ) {
    PromotionChannelShopPo promotionChannelShopPo = iterator.next();
    String channelCode = promotionChannelShopPo.getChannel();
    String storeCode = promotionChannelShopPo.getStoreCode();
    if (!processChannels.contains(channelCode)) {
       logger.info("促销规则ID [{}], 不支持处理渠道 [{}}", ruleId, channelCode);
       iterator.remove();
    }
    if (!storeCodes.contains(storeCode)) {
        logger.info("促销规则ID [{}], 不支持处理门店 [{}}", ruleId, storeCode);
        iterator.remove();
    }
}

抛出java.lang.IllegalStateException异常代码出现在第二个iterator.remove();

我们知道使用增强for循环删除list后继续遍历会抛出 ConcurrentModificationException
按理说使用迭代器删除是不会出现异常的啊,对伐

不过既然出现,我们看下迭代器的remove方法,它是怎么抛出IllegalStateException 不就知道原因了嘛

首先 Iterator 是一个接口,所以我们看迭代的对象ArrayList,
所以,我们应该查看的对象是 ArrayList.Itr.remove 方法

// index of next element to return
int cursor;
// index of last element returned; -1 if no such
int lastRet = -1; 

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;
    // 赋值 lastRet 属性
    return (E) elementData[lastRet = i];
}

public void remove() {
    if (lastRet < 0)
        // 抛出异常
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        // 重置 lastRet 属性
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

很明显 ,当lastRet < 0 会抛出 IllegalStateException。
而属性lastRet默认值为-1,只有当调用next方法,会把当前游标cursor赋值到lastRet。
同时删除会重置lastRet为-1。

结论,使用迭代器删除元素:
1、必须调用过一次next方法,才能调用remove方法。
2、调用remove方法后,不能再次删除同一元素。