Java | コピーを使った Undo 機能

Java Java
スポンサーリンク

では「もっと複雑なオブジェクト(多次元配列や複合クラス)でのUndo/Redo」の例を紹介します。ここでは 多次元配列を持つ複合クラス を深いコピーしながら履歴管理し、Undo/Redoを実現する方法を解説します。


シナリオ

  • GameBoard クラスを作成。
    • フィールド: String name(盤面名)、int[][] cells(2次元配列で盤面の状態)。
  • GameBoard[] 配列を「状態」として扱う。
  • 状態を変更するたびに 深いコピー を履歴に保存。
  • Undo/Redoで盤面の状態を行ったり来たりできる。

コード例:複合クラス+多次元配列のUndo/Redo

import java.util.*;

class GameBoard {
    String name;
    int[][] cells;

    GameBoard(String name, int[][] cells) {
        this.name = name;
        // 深いコピーして初期化
        this.cells = new int[cells.length][];
        for (int i = 0; i < cells.length; i++) {
            this.cells[i] = Arrays.copyOf(cells[i], cells[i].length);
        }
    }

    // 深いコピー用コンストラクタ
    GameBoard(GameBoard other) {
        this.name = other.name;
        this.cells = new int[other.cells.length][];
        for (int i = 0; i < other.cells.length; i++) {
            this.cells[i] = Arrays.copyOf(other.cells[i], other.cells[i].length);
        }
    }

    @Override
    public String toString() {
        return name + " " + Arrays.deepToString(cells);
    }
}

public class UndoRedoComplex {
    public static void main(String[] args) {
        GameBoard[] state = {
            new GameBoard("Board1", new int[][]{{1, 2}, {3, 4}}),
            new GameBoard("Board2", new int[][]{{5, 6}, {7, 8}})
        };

        List<GameBoard[]> undoStack = new ArrayList<>();
        List<GameBoard[]> redoStack = new ArrayList<>();

        // 初期状態を保存
        undoStack.add(deepCopy(state));

        // ---- 状態変更 ----
        state[0].cells[0][0] = 99; // Board1の左上を変更
        undoStack.add(deepCopy(state));

        state[1].cells[1][1] = 77; // Board2の右下を変更
        undoStack.add(deepCopy(state));

        System.out.println("現在の状態: " + Arrays.toString(state));
        // [Board1 [[99, 2], [3, 4]], Board2 [[5, 6], [7, 77]]]

        // ---- Undo ----
        state = undo(state, undoStack, redoStack);
        System.out.println("Undo後: " + Arrays.toString(state));
        // [Board1 [[99, 2], [3, 4]], Board2 [[5, 6], [7, 8]]]

        state = undo(state, undoStack, redoStack);
        System.out.println("さらにUndo後: " + Arrays.toString(state));
        // [Board1 [[1, 2], [3, 4]], Board2 [[5, 6], [7, 8]]]

        // ---- Redo ----
        state = redo(state, undoStack, redoStack);
        System.out.println("Redo後: " + Arrays.toString(state));
        // [Board1 [[99, 2], [3, 4]], Board2 [[5, 6], [7, 8]]]

        state = redo(state, undoStack, redoStack);
        System.out.println("さらにRedo後: " + Arrays.toString(state));
        // [Board1 [[99, 2], [3, 4]], Board2 [[5, 6], [7, 77]]]
    }

    // 深いコピー(GameBoard配列)
    private static GameBoard[] deepCopy(GameBoard[] src) {
        GameBoard[] copy = new GameBoard[src.length];
        for (int i = 0; i < src.length; i++) {
            copy[i] = new GameBoard(src[i]); // 新しいGameBoardを作成
        }
        return copy;
    }

    // Undo処理
    private static GameBoard[] undo(GameBoard[] state, List<GameBoard[]> undoStack, List<GameBoard[]> redoStack) {
        if (undoStack.size() > 1) {
            redoStack.add(deepCopy(state));
            undoStack.remove(undoStack.size() - 1);
            return deepCopy(undoStack.get(undoStack.size() - 1));
        }
        return state;
    }

    // Redo処理
    private static GameBoard[] redo(GameBoard[] state, List<GameBoard[]> undoStack, List<GameBoard[]> redoStack) {
        if (!redoStack.isEmpty()) {
            GameBoard[] redoState = redoStack.remove(redoStack.size() - 1);
            undoStack.add(deepCopy(redoState));
            return redoState;
        }
        return state;
    }
}
Java

解説

  • 深いコピー:
    • GameBoardcells は2次元配列なので、外側だけでなく内側もコピーする必要がある。
    • コンストラクタで Arrays.copyOf を使って各行を複製。
  • Undo:
    • 最新状態を redoStack に保存 → undoStack の1つ前を復元。
  • Redo:
    • redoStack から取り出して復元 → undoStack に戻す。
  • 結果:
    • 多次元配列を持つ複合クラスでも、完全に独立した履歴が作れる。

応用ポイント

  • ゲーム盤面管理: 将棋やオセロなどの盤面をUndo/Redoできる。
  • シミュレーション: 複雑な状態(複数のオブジェクト+多次元配列)を履歴管理できる。
  • テキストエディタ風: 複数のドキュメントやページを同時に管理してUndo/Redo可能。

✅ これで「複合クラス+多次元配列のUndo/Redo」の完全版が完成です。

Java
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました