ListIterator.remove抛IllegalStateException及set调用后无异常原因解析
为什么ListIterator调用add后remove抛异常,set后remove却正常?
这事儿得从ListIterator的规则限制说起,尤其是remove()方法的调用前提——它可不是随时能调用的!
先搞懂remove()的核心要求
在ArrayList的ListIterator实现里,remove()有个硬性规则:只能在刚刚执行过next()或者previous()之后调用,而且中间不能穿插add()或者另一个remove()操作。
这背后是迭代器内部维护的一个lastRet变量,它记录了上一次通过next()/previous()访问的元素索引,remove()就是靠这个索引来定位要删除的元素。如果lastRet是-1(表示没有最近访问的元素),调用remove()就会直接抛出IllegalStateException。
第一段代码(add后remove)的问题分析
我们拆解这段代码的执行流程:
public class App { public static void main( String[] args ) throws InterruptedException { List<Integer> list = new ArrayList<>(); list.add(3); list.add(2); list.add(1); ListIterator<Integer> listIterator = list.listIterator(); while(listIterator.hasNext()){ listIterator.next(); // 1. 调用next(),lastRet被设为当前元素索引(比如第一次循环是0) listIterator.add(5); // 2. 调用add(),插入元素后,lastRet被重置为-1! listIterator.remove(); // 3. 此时lastRet是-1,迭代器找不到要删的元素,抛出异常 } } }
关键就在add()操作:它会把新元素插入到当前迭代器位置的前方,同时主动把lastRet重置为-1——因为add()并没有“选中”任何一个现有元素,迭代器的状态变成了“没有最近访问的元素”,这时候调用remove()自然不符合规则。
第二段代码(set后remove)正常的原因
再看这段代码的流程:
public class App { public static void main( String[] args ) throws InterruptedException { List<Integer> list = new ArrayList<>(); list.add(3); list.add(2); list.add(1); ListIterator<Integer> listIterator = list.listIterator(); while(listIterator.hasNext()){ listIterator.next(); // 1. 调用next(),lastRet被设为当前元素索引 listIterator.set(5); // 2. 调用set(),只是修改lastRet指向的元素值,lastRet保持不变 listIterator.remove(); // 3. 迭代器通过lastRet找到要删的元素(刚修改的那个),正常执行 } } }
set()操作的本质是修改已选中的元素,它不会改变lastRet的状态——lastRet依然保留着上一次next()访问的元素索引。所以这时候调用remove(),迭代器能准确找到要删除的元素,自然不会抛出异常。
总结一下
add()会改变迭代器的状态,清除上一次访问的元素标记(lastRet=-1),导致后续remove()找不到操作对象;set()只是修改已选中的元素,不会改变迭代器的访问状态,所以remove()依然能基于之前的lastRet执行操作。
内容的提问来源于stack exchange,提问作者Karunakar Reddy L
相关产品推荐
相关产品推荐

