
本文介绍一种绕过 ListIterator 光标机制限制的方法,通过封装状态实现真正意义上的 next() 和 previous() 双向访问,并能连续多次调用 previous() 返回相同元素(如期望输出 1, 2, 1),适用于 Android/Kotlin 场景。
本文介绍一种绕过 `listiterator` 光标机制限制的方法,通过封装状态实现真正意义上的 `next()` 和 `previous()` 双向访问,并能连续多次调用 `previous()` 返回相同元素(如期望输出 `1, 2, 1`),适用于 android/kotlin 场景。
ListIterator 的设计本质是游标居中模型:它不维护“当前元素”,而是始终位于两个元素之间。因此 next() 和 previous() 并非对称地读取“当前项”,而是分别读取游标右侧/左侧的元素。这导致连续两次 previous() 不会返回同一值(第一次回退到前一项,第二次才继续回退),与直觉中的“返回上一个已访问元素”不符。
若坚持使用标准 API,最简洁的合规解法是手动缓存上一个 next() 结果,构建一个轻量级、无副作用的双向导航器:
class BacktrackableIterator<t>(private val list: List<t>) {
private var index = -1
private var lastReturned: T? = null
fun next(): T? {
if (index + 1 >= list.size) return null
lastReturned = list[++index]
return lastReturned
}
fun previous(): T? {
if (index = 0) list[index] else null
}
// 使用示例
val list = listOf(1, 2, 3, 4, 5)
val it = BacktrackableIterator(list)
println(it.next()) // 1
println(it.next()) // 2
println(it.peekPrevious()) // 2(只查看,不移动)
println(it.previous()) // 1
println(it.previous()) // null(已到开头,安全返回 null)</t></t>
该实现明确区分了:
-
next():前进并返回新元素; -
previous():后退并返回前一个元素; -
peekPrevious():仅查看上一个已访问元素(即最近一次next()的结果),不改变位置 —— 这正是实现1, 2, 1行为的关键。
⚠️ 注意事项:
- 避免在空列表或越界时调用
next()/previous()导致IndexOutOfBoundsException,本实现统一返回null(可按需改为抛出异常或使用Optional<t></t>); - 若需线程安全,应额外加锁或改用
AtomicInteger管理index; - 此方案不修改原列表,符合函数式编程习惯,也便于单元测试。
总结:ListIterator 的语义不可更改,但通过封装索引与缓存状态,我们能以清晰、可控、零依赖的方式达成“记忆式双向遍历”。对于 UI 分页、步骤导航、撤销重做等场景,此类定制迭代器比强行适配 ListIterator 更健壮、更易维护。










