用deque实现撤销重做需双栈:undostack存待撤操作,redostack存已撤待重做操作;新操作压undostack,撤销时弹出执行undo()并压入redostack,重做则反向操作;命令对象须实现execute()和undo()接口,并清空redostack以防止误重做。

用 Deque 实现撤销操作,核心是利用其“两端进出”的特性:新操作压入队尾(push 或 addLast),撤销时从队尾弹出(pop 或 removeLast);重做则把刚撤销的操作暂存于另一个 Deque(即“重做栈”),需要时再从该栈顶取回。
用 ArrayDeque 作为主撤销栈
ArrayDeque 是 Deque 最常用实现,非线程安全但性能好、支持 null 元素(注意:若需存 null,应改用 LinkedList,因 ArrayDeque 不允许 null)。撤销逻辑本质是后进先出(LIFO),所以把 Deque 当作栈用即可:
- 执行新操作 → 将操作封装对象(如
Command)调用undoStack.push(command) - 触发撤销 →
if (!undoStack.isEmpty()) undoStack.pop().undo() - 每次撤销后,把刚弹出的命令加入
redoStack(同为Deque)以便重做
搭配一个重做栈(Redo Stack)形成闭环
仅靠一个 Deque 只能撤,无法重做。必须维护两个栈:undoStack 存待撤销操作,redoStack 存已撤销、待重做的操作:
- 撤销时:从
undoStack弹出 → 执行undo()→ 推入redoStack - 重做时:从
redoStack弹出 → 执行execute()(或redo())→ 推入undoStack - 执行新操作前,清空
redoStack(防止用户在新操作后误重做旧路径)
命令对象需统一接口,支持正向与逆向执行
每个操作应封装为实现 Command 接口的对象,至少包含两个方法:
-
execute():执行当前操作(如插入文本、移动元素) -
undo():执行其逆操作(如删除刚插入的文本、移回原位置)
例如,文本编辑中“输入字符 'a'”的命令,undo() 就是“删除末尾字符”。关键在于:每个命令自身保存足够上下文(如光标位置、原值),确保 undo() 可靠还原状态。
边界处理与内存控制建议
实际使用中需避免栈无限增长:
- 限制栈大小:插入前检查
undoStack.size() >= MAX_HISTORY,超限时undoStack.pollLast()(丢弃最早操作) - 合并连续操作:如连续输入多个字符,可合并为单个“批量输入命令”,减少栈深度
- 撤销/重做按钮状态同步:UI 层监听
undoStack.isEmpty()和redoStack.isEmpty()动态启用/禁用按钮
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











