Java | ジャグな3D配列の可視化ツール(ASCII)を実装

Java Java
スポンサーリンク

ジャグ(行や列の長さが行ごと・層ごとにバラバラ)な3次元配列を 層ごとに読みやすい ASCII テーブル として表示する実用的な Java 実装を出します。
特徴:

  • 各層(layer) を別々のテーブルとして表示
  • 各層の 列数はその層内の最大列数 に合わせて自動調整
  • 「その行に列がない」場合は空セルとして表示(ジャグ対応)
  • null 層や null 行にも安全に対応
  • 数字・文字列どちらも表示可能(Object を扱う汎用版)

以下は 完全動作するサンプルコード(コメント付き)です。コピーして javac / java で試せます。

import java.util.Arrays;

public class Jagged3DAsciiVisualizer {

    /**
     * 汎用的に Object[][][] (ジャグな3D) を表示するメソッド。
     * 数値や文字列など Object を toString() して表示する。
     */
    public static void print3DJagged(Object[][][] data, String layerTitlePrefix) {
        if (data == null || data.length == 0) {
            System.out.println("(empty 3D array)");
            return;
        }

        for (int l = 0; l < data.length; l++) {
            Object[][] layer = data[l];

            // 表題
            System.out.println();
            System.out.println(layerTitlePrefix + " " + l);

            if (layer == null || layer.length == 0) {
                System.out.println("(empty layer)");
                continue;
            }

            // その層の最大列数を計算(ジャグ対応)
            int maxCols = 0;
            for (int r = 0; r < layer.length; r++) {
                Object[] row = layer[r];
                if (row != null && row.length > maxCols) maxCols = row.length;
            }

            if (maxCols == 0) {
                // 全行が null または長さ0
                System.out.println("(no columns in this layer)");
                continue;
            }

            // 各列の幅(列ごとに最大の文字幅を求める)
            int[] colWidths = new int[maxCols];
            for (int c = 0; c < maxCols; c++) {
                int maxW = 0;
                for (int r = 0; r < layer.length; r++) {
                    Object[] row = layer[r];
                    String s = (row != null && c < row.length && row[c] != null) ? row[c].toString() : "";
                    if (s.length() > maxW) maxW = s.length();
                }
                colWidths[c] = Math.max(maxW, 1); // 幅0は見づらいので1以上
            }

            // 横罫線を出すためのヘルパー
            StringBuilder lineBuilder = new StringBuilder();
            lineBuilder.append("+");
            for (int c = 0; c < maxCols; c++) {
                int w = colWidths[c] + 2; // 左右に1スペースずつ
                for (int k = 0; k < w; k++) lineBuilder.append("-");
                lineBuilder.append("+");
            }
            String horizontalLine = lineBuilder.toString();

            // ヘッダ(列番号)を出したい場合はここで出せる(任意)
            // 例: | c0 | c1 | c2 |
            StringBuilder headerBuilder = new StringBuilder();
            headerBuilder.append("|");
            for (int c = 0; c < maxCols; c++) {
                String label = "c" + c;
                headerBuilder.append(" ").append(padCenter(label, colWidths[c])).append(" ").append("|");
            }
            String header = headerBuilder.toString();

            // 実際の行出力
            System.out.println(horizontalLine);
            System.out.println(header);
            System.out.println(horizontalLine);

            for (int r = 0; r < layer.length; r++) {
                Object[] row = layer[r];
                StringBuilder rowBuilder = new StringBuilder();
                rowBuilder.append("|");
                for (int c = 0; c < maxCols; c++) {
                    String s = (row != null && c < row.length && row[c] != null) ? row[c].toString() : "";
                    rowBuilder.append(" ").append(padRight(s, colWidths[c])).append(" ").append("|");
                }
                System.out.println(rowBuilder.toString());
                System.out.println(horizontalLine);
            }
        }
    }

    // 右寄せパディング(幅を満たすため右にスペース)
    private static String padRight(String s, int width) {
        if (s == null) s = "";
        StringBuilder sb = new StringBuilder(s);
        while (sb.length() < width) sb.append(' ');
        return sb.toString();
    }

    // 中央揃え(ヘッダ表示用。必要なければ使わない)
    private static String padCenter(String s, int width) {
        if (s == null) s = "";
        if (s.length() >= width) return s;
        int total = width - s.length();
        int left = total / 2;
        int right = total - left;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < left; i++) sb.append(' ');
        sb.append(s);
        for (int i = 0; i < right; i++) sb.append(' ');
        return sb.toString();
    }

    // サンプル実行用 main
    public static void main(String[] args) {
        Object[][][] jagged = new Object[][][] {
            {   // layer 0
                {1, 200, 3},
                {4567, 8}
            },
            null, // layer 1 is null (テスト)
            {   // layer 2 (rows have different lengths)
                {"A", "B", "C", "D"},
                {"longText", "x"},
                {} // 空行
            },
            {   // layer 3 (all null rows)
                null,
                null
            },
            {   // layer 4 (single-row single-col)
                {42}
            }
        };

        print3DJagged(jagged, "Layer");
    }
}
JavaScript

サンプル出力(上の main を実行したときの一例)

Layer 0
+------+-----+----+
|  c0  | c1  | c2 |
+------+-----+----+
| 1    | 200 | 3  |
+------+-----+----+
| 4567 | 8   |    |
+------+-----+----+

Layer 1
(empty layer)

Layer 2
+----------+----+----+----+
|   c0     | c1 | c2 | c3 |
+----------+----+----+----+
| longText | x  |    |    |
+----------+----+----+----+
| A        | B  | C  | D  |
+----------+----+----+----+
|          |    |    |    |
+----------+----+----+----+

Layer 3
(no columns in this layer)

Layer 4
+----+
| c0 |
+----+
| 42 |
+----+

(注:実行マシンや文字列の順序でヘッダの行順が変わる場合があります。上は例示目的の出力)


カスタマイズ案(必要なら実装します)

  • ヘッダ(列番号)を消す/カスタムラベルにする
  • 数字を右寄せ、文字列を左寄せにする(今は全セル左寄せにしている)
  • ANSI カラーで強調(端末が対応していれば)
  • CSV へ出力する機能を追加する
  • 2次元 ASCII 出力メソッドを切り出して、層ごとに呼び出すように簡潔化する

注意点(初心者向け)

  • ジャグ配列は行・列・層ごとに nulllength==0 が出るので、アクセス前に null チェックを入れている点に注目してください。
  • 表示は toString() を使っているので、オブジェクトを入れる場合は toString() の挙動に依存します。必要なら表示用に変換(例:null"(null)")して下さい。

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