Java 逆引き集 | Stream を使った行列変換(transpose) — データ整形

Java Java
スポンサーリンク

Stream を使った行列変換(transpose) — データ整形

「行列の転置(transpose)」とは、行と列を入れ替える操作です。
例えば 2×3 の行列を転置すると 3×2 になります。Stream を使うと、入れ子のリストを柔軟に整形できます。


基本イメージ

  • 入力: List<List<T>> (行ごとのリスト)
  • 出力: List<List<T>> (列ごとのリスト)

例:

[[1,2,3],
 [4,5,6]]

→ 転置すると

[[1,4],
 [2,5],
 [3,6]]

基本コード例

1) Stream を使った transpose

import java.util.*;
import java.util.stream.*;

public class TransposeExample {
    public static void main(String[] args) {
        List<List<Integer>> matrix = List.of(
            List.of(1,2,3),
            List.of(4,5,6)
        );

        int rows = matrix.size();
        int cols = matrix.get(0).size();

        List<List<Integer>> transposed =
            IntStream.range(0, cols)
                     .mapToObj(c ->
                         IntStream.range(0, rows)
                                  .mapToObj(r -> matrix.get(r).get(c))
                                  .toList()
                     )
                     .toList();

        System.out.println(transposed); // [[1,4],[2,5],[3,6]]
    }
}
Java
  • ポイント:
    • 外側の IntStream.range(0, cols) が「列インデックス」
    • 内側の IntStream.range(0, rows) が「行インデックス」
    • mapToObj で列ごとのリストを作る

例題で理解する

例題1: 文字列の表を転置

List<List<String>> table = List.of(
    List.of("Name","Age"),
    List.of("Tanaka","20"),
    List.of("Sato","25")
);

List<List<String>> transposed =
    IntStream.range(0, table.get(0).size())
             .mapToObj(c ->
                 IntStream.range(0, table.size())
                          .mapToObj(r -> table.get(r).get(c))
                          .toList()
             )
             .toList();

System.out.println(transposed);
// [[Name, Tanaka, Sato], [Age, 20, 25]]
Java
  • 用途: CSV や表データの「行列入れ替え」に便利。

例題2: 可変長行列(行ごとに列数が違う)

List<List<Integer>> jagged = List.of(
    List.of(1,2),
    List.of(3,4,5)
);

int maxCols = jagged.stream().mapToInt(List::size).max().orElse(0);

List<List<Integer>> transposed =
    IntStream.range(0, maxCols)
             .mapToObj(c ->
                 jagged.stream()
                       .map(row -> c < row.size() ? row.get(c) : null) // 足りない部分は null
                       .toList()
             )
             .toList();

System.out.println(transposed);
// [[1,3],[2,4],[null,5]]
Java
  • ポイント: 行ごとに列数が違う場合は「null やデフォルト値」で埋める。

例題3: 2次元配列から転置

int[][] matrix = {
    {1,2,3},
    {4,5,6}
};

int rows = matrix.length;
int cols = matrix[0].length;

int[][] transposed = 
    IntStream.range(0, cols)
             .mapToObj(c -> IntStream.range(0, rows)
                                     .map(r -> matrix[r][c])
                                     .toArray())
             .toArray(int[][]::new);

System.out.println(Arrays.deepToString(transposed));
// [[1,4],[2,5],[3,6]]
Java

テンプレート集

  • 基本転置(List<List<T>>)
List<List<T>> transposed =
    IntStream.range(0, cols)
             .mapToObj(c -> IntStream.range(0, rows)
                                     .mapToObj(r -> matrix.get(r).get(c))
                                     .toList())
             .toList();
Java
  • 可変長行列対応
IntStream.range(0, maxCols)
         .mapToObj(c -> matrix.stream()
                              .map(row -> c < row.size() ? row.get(c) : defaultVal)
                              .toList())
         .toList();
Java
  • 配列版
int[][] transposed =
    IntStream.range(0, cols)
             .mapToObj(c -> IntStream.range(0, rows)
                                     .map(r -> matrix[r][c])
                                     .toArray())
             .toArray(int[][]::new);
Java

落とし穴と回避策

  • 列数が揃っている前提: List<List<T>> の各行が同じサイズでないと IndexOutOfBoundsException。可変長なら maxCols を計算して埋める。
  • null の扱い: 可変長行列では「欠けた部分」を null やデフォルト値で埋める設計が必要。
  • 大規模データ: Stream のネストは可読性は高いが、パフォーマンスは for ループと同等。大量データでは計測して選択。
  • プリミティブ vs オブジェクト: 配列はプリミティブ型で効率的、List は柔軟性が高い。用途に応じて選ぶ。

まとめ

  • Stream の IntStream.range を使えば「行と列の入れ替え」を簡潔に書ける。
  • 可変長行列や配列にも応用可能。
  • データ整形やレポート生成で「行列転置」が必要な場面に役立つ。

👉 練習課題: 「CSV ファイルを読み込み、行ごとの List<List<String>> を作り、Stream で転置して列ごとの統計を計算」してみましょう。行列転置の実用性が体感できます。

タイトルとURLをコピーしました