listiterator 可作为撤销/重做中的状态指针,通过指向不可变快照列表的游标实现前后跳转;其 nextindex() 即已执行操作数,无需额外变量,兼顾语义清晰与性能稳定。

直接用 ListIterator 实现撤销/重做中的“状态指针移动”,本质是把编辑历史建模为有序不可变快照列表,再用双向迭代器充当可前后跳转的游标——它不改写原始数据,而是动态切换当前激活状态。
用 ListIterator 管理历史快照链
每次用户执行操作(如输入、删除),生成一个只读的文本快照(如 String 或轻量 DocumentState 对象),追加到 ArrayList<state></state> 中。关键不是修改列表,而是让 ListIterator 始终指向“当前可见状态”的索引位置:
- 初始时调用
list.listIterator(),迭代器位于索引 0 前(即“无当前状态”);首次操作后,it.next()移动到索引 0,此时为当前态 - 撤销时调用
it.hasPrevious() && it.previous(),指针回退,返回上一快照;重做则用it.hasNext() && it.next() - 新操作发生时,先
it.next()到末尾(若未在尾部),再list.subList(it.nextIndex(), list.size()).clear()清除“未来分支”,最后list.add(newState)并it.next()落位
避免并发与失效:状态不可变 + 迭代器单点持有
ListIterator 不是线程安全的,也不支持列表结构变更后的自动同步。实战中必须约束两点:
- 所有快照对象设计为不可变(final 字段、无 setter),确保即使多个模块引用同一快照也不会意外污染
- 整个编辑器后台只持有一个
ListIterator实例,封装在HistoryManager类中,所有撤销/重做/新增操作都通过它统一调度,禁止外部直接操作底层ArrayList - 当需要保存/加载历史时,序列化的是快照列表本身,而非迭代器——恢复时重建
ListIterator并用it.skip(n)(或循环next())快速定位到上次位置
指针位置即状态语义:nextIndex() 就是“已应用操作数”
it.nextIndex() 的值天然对应“从初始空文档起,已顺序执行的操作数量”,这个整数可直接用于 UI 显示(如“已撤销 2 步”)、权限控制(如禁止对前 5 步做重做)或 diff 计算(比较 get(it.nextIndex()-1) 和 get(it.nextIndex()-2) 的差异)。
不需要额外维护 currentStep 变量——迭代器自身就是状态指针,它的位置就是权威事实。
注意边界与性能:小列表够用,大历史需分页
ArrayList + ListIterator 在几千步内性能稳定(O(1) 随机访问 + O(1) 迭代移动)。但若允许万级操作:
- 避免长期累积——可设置最大历史长度,新增时自动 trim 掉最旧项(
list.remove(0)),并相应调整迭代器位置(如原在索引 500,删首项后变为 499) - 不推荐改用
LinkedList:虽然ListIterator在链表中增删快,但随机定位(如跳转到第 1000 步)是 O(n),反而更慢 - 真有超长历史需求(如协同编辑日志),应分层处理:内存保留最近 200 步 +
ListIterator管理,更早记录存磁盘,按需加载










