list.listIterator(list.size()) 是正确起点(游标在末尾后),必须先 hasPrevious() 判断再 previous() 取值;若用 next() 会正向走,与逆序目标相反。

为什么不能直接用 listIterator() 从末尾开始遍历?
因为 listIterator() 默认返回的是指向索引 0 的迭代器,不是末尾。想逆序遍历,必须显式指定起始位置——即传入 list.size() 作为参数,让迭代器初始位于“末尾之后”的位置,再用 hasPrevious() + previous() 往回走。
常见错误是传 list.size() - 1,结果会跳过最后一个元素;或者没调用 hasPrevious() 就直接 previous(),抛出 NoSuchElementException。
-
list.listIterator(list.size())是正确起点(游标在末尾后) - 必须先
hasPrevious()判断,再previous()取值 - 若用
next()会正向走,和“逆序”目标相反
如何安全地双向切换遍历方向?
ListIterator 支持在一次遍历中随时切换方向,但要注意游标位置是共享的。比如先 next() 走了两步,再调 previous() 会回到上一个元素,不是从头重新逆序。
典型场景:查找某元素后,立刻向前检查前一个是否满足条件。这时不能新建迭代器,否则丢失上下文。
- 切换前确认
hasNext()或hasPrevious()返回true -
nextIndex()和previousIndex()可帮你定位当前游标逻辑位置 - 多次
previous()后再next(),取到的是刚跳过的那个元素(游标可来回抖动)
性能与兼容性要注意什么?
ArrayList 的 ListIterator 随机访问是 O(1),逆序遍历和正序一样快;但 LinkedList 虽然也支持 ListIterator,其 previous() 是 O(1)(链表天然支持双向),不过频繁调用仍比数组略慢,因涉及指针跳转。
不支持的集合如 CopyOnWriteArrayList 虽有 listIterator(),但其迭代器是快照式的,遍历时无法反映后续修改;Arrays.asList() 返回的列表也支持,但底层是固定数组,增删会抛 UnsupportedOperationException。
- 避免在遍历中调用
list.remove()—— 应该用迭代器自己的remove() -
add()和set()在ListIterator中可用,但语义依赖具体实现(如ArrayList的add()是插入到游标前) - Java 8+ 中,若只是逆序读取,
Collections.reverseOrder()配合流式操作更简洁,但那是新副本,不适用于原地遍历场景
一个防错的逆序遍历模板
以下代码能稳定工作,且覆盖空列表、单元素等边界:
List<string> list = Arrays.asList("a", "b", "c");
ListIterator<string> lit = list.listIterator(list.size());
while (lit.hasPrevious()) {
String item = lit.previous();
System.out.println(item); // 输出 c, b, a
}
</string></string>
关键点在于:起点用 size(),循环条件只依赖 hasPrevious(),不查索引、不计数、不假设非空。一旦需要边遍历边修改,就把 lit.remove() 或 lit.add(...) 放在 previous() 后面,且注意它们会影响后续 hasPrevious() 的判断结果。
真正容易被忽略的是:ListIterator 的行为高度依赖底层实现,同一个接口,在 ArrayList 和 LinkedList 中游标移动的底层开销不同,但 API 表现一致——所以写业务逻辑时别猜性能,要测。










