listiterator 仅适用于 list 实现类(如 arraylist、linkedlist),因其依赖索引和双向移动能力;set 无序、map 非 collection 子接口,故不支持。

为什么 ListIterator 不能用在 Set 或 Map 上
因为 ListIterator 是 List 接口专属的迭代器,它依赖索引位置(nextIndex()、previousIndex())和双向移动能力,而 Set 无序、Map 不是 Collection 子接口,它们的 iterator() 只返回普通 Iterator。
常见错误现象:java.lang.ClassCastException: java.util.HashMap$KeyIterator cannot be cast to java.util.ListIterator —— 这通常发生在你对 map.keySet().iterator() 强转成 ListIterator 时。
- 只有实现了
List的集合(如ArrayList、LinkedList)调用listIterator()才返回合法的ListIterator -
Arrays.asList()返回的列表支持ListIterator;但Stream.toList()(Java 16+)返回的是不可变列表,调用listIterator()会抛UnsupportedOperationException - 如果需要对非
List结构做双向遍历,得先转成ArrayList:比如new ArrayList(set),但要注意这会额外占用内存
next() 和 previous() 的初始位置陷阱
ListIterator 创建后,初始“光标”在索引 0 之前。这意味着第一次调用 next() 返回索引 0 元素,而第一次调用 previous() 会抛 NoSuchElementException —— 因为前面没元素。
容易踩的坑:写反向遍历时直接 while (it.hasPrevious()) { it.previous(); } 没问题,但若中间混用 next(),光标偏移容易失控。
- 安全的反向遍历写法是先调用
it.last()(仅LinkedList支持)或用list.size()初始化:ListIterator<string> it = list.listIterator(list.size());</string> -
it.nextIndex()返回下一个next()将取到的索引;it.previousIndex()返回下一个previous()将取到的索引;两者始终相差 1 - 在循环中交替调用
next()和previous()会导致元素重复访问或跳过,除非你明确在模拟“回退一步”逻辑
add()、set()、remove() 的触发时机与限制
ListIterator 的结构修改方法不是“随时可用”,而是严格绑定在上一次 next() 或 previous() 之后 —— 它们操作的是“刚刚访问过的那个元素”的邻近位置。
典型错误:it.add("x"); it.add("y"); 连续两次 add() 会抛 IllegalStateException,因为第二次调用前没有再次移动光标。
-
add()总是插入到当前光标位置(即下一次next()将返回的位置),不会影响已遍历元素 -
set()只能修改上一次next()或previous()返回的元素;如果刚创建迭代器或刚调用过add(),再调set()也会抛异常 -
remove()删除的是上一次访问的元素,且每个元素只能被删一次;删完后光标自动“补位”,后续next()会跳过原下一个元素
用 LinkedList 还是 ArrayList?性能差异在哪
ListIterator 在两种实现上的行为一致,但底层效率差别明显:在 ArrayList 中,previous() 平均时间复杂度是 O(n),因为要从头开始算索引;而 LinkedList 是 O(1),靠双向链表指针直接跳。
如果你频繁反向遍历或在中间大量插入/删除,LinkedList 更合适;但注意,它的随机访问(get(int))是 O(n),所以别用 list.get(i) 配合 ListIterator 混用。
-
ArrayList.listIterator(int index)是 O(1),但LinkedList.listIterator(int index)是 O(n),因为要从头或尾遍历到该位置 - 想高效双向遍历整个列表?优先选
LinkedList+listIterator();想快速随机访问+偶尔反向?用ArrayList+ 正向遍历 + 索引运算更稳 - JDK 21 的
SequencedCollection接口提供了reversed()方法,但返回的是新视图,不支持ListIterator的原地修改语义
真正难处理的是边遍历边修改还要求稳定索引——这时候别硬扛 ListIterator,改用传统 for 循环配合 list.removeIf() 或收集待删索引再批量处理,反而更清晰可靠。










