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 で転置して列ごとの統計を計算」してみましょう。行列転置の実用性が体感できます。
