2次元配列の全体像
2次元配列は「配列の配列」です。行(外側)と列(内側)という2軸を持つ構造で、matrix[row][col] のように2つの添字で要素へアクセスします。Java の2次元配列は「固定長の1次元配列を複数並べたもの」なので、行ごとに列数が異なる「ジャグ配列」も作れます。重要なのは「長さの参照の仕方」「境界(オフバイワン)」「初期化方法」を正しく押さえることです。
宣言・生成・初期化の基本
宣言とサイズ指定での生成
int[][] m; // 2次元配列(int の配列の配列)への参照を宣言
m = new int[2][3]; // 2行×3列を生成(全要素は 0)
m[0][1] = 42; // 1行目2列目へ代入
Java外側(行数)と内側(列数)を同時に指定すると「長方形」の配列ができます。要素の初期値は型のデフォルト値(数値は 0、boolean は false、参照型は null)です。
初期化リテラルで見た目どおりに
int[][] m = {
{1, 2, 3},
{4, 5, 6}
};
System.out.println(m[1][2]); // 6(2行目3列目)
Java行ごとに { ... } で並べると、直感的な形で宣言と初期化をまとめて書けます。読みやすく、境界の把握もしやすい方法です。
長さの参照と安全な走査(重要ポイントの深掘り)
行数と列数の取り方
int rows = m.length; // 行数
int cols = m[0].length; // 0行目の列数(長方形の場合)
Javaジャグ配列では行ごとの列数が異なるため、「その行の length」を必ず参照します。固定観念で列数を決め打ちすると範囲外アクセスの原因になります。
for (int r = 0; r < m.length; r++) {
for (int c = 0; c < m[r].length; c++) {
// 安全に走査(各行の長さを見る)
}
}
Java未満条件でオフバイワンを防ぐ
終了条件は「未満(<)」が基本です。<= を使うと、最後の次を踏んで ArrayIndexOutOfBoundsException になります。最後の要素へアクセスしたい場合は「length - 1」を使います。
ジャグ配列(行ごとに列数が違う)と使い分け
ジャグ配列の生成とアクセス
int[][] jag = new int[3][];
jag[0] = new int[1];
jag[1] = new int[2];
jag[2] = new int[4];
for (int r = 0; r < jag.length; r++) {
for (int c = 0; c < jag[r].length; c++) {
System.out.printf("[%d,%d]=%d%n", r, c, jag[r][c]);
}
}
Java「行ごとに長さが違う」形が必要なときに有効です。表のような完全な長方形が必要なら、同じ列数で生成しましょう。可変のデータには List<List<...>> も選択肢です。
参照型・プリミティブ型の初期値と null 安全
参照型の2次元配列
String[][] names = new String[2][3]; // すべて null
if (names[0][0] == null) {
names[0][0] = "sato"; // 値を入れてから使う
}
System.out.println(names[0][0].toUpperCase()); // "SATO"
Java参照型は生成直後は null です。メソッド呼び出しやプロパティアクセスをする前に、必ず null を排除するか初期値を入れておきます。プリミティブ型(int など)は 0 から始まります。
典型操作とパターン
全要素の合計・平均
int[][] m = {{1,2,3},{4,5,6}};
int sum = 0, count = 0;
for (int r = 0; r < m.length; r++) {
for (int c = 0; c < m[r].length; c++) {
sum += m[r][c];
count++;
}
}
double avg = sum / (double) count;
System.out.printf("sum=%d avg=%.2f%n", sum, avg);
Java「位置によらず全件処理」は各行の length を見ながらネストで走査するのが定石です。
行・列の取り出し
int[][] m = {{1,2,3},{4,5,6}};
int[] row1 = m[1]; // 2行目(配列そのものを参照)
int col2_sum = 0;
for (int r = 0; r < m.length; r++) {
col2_sum += m[r][1]; // 2列目の合計(列は固定添字)
}
Java行は「配列の配列」特性を活かしてそのまま参照できます。列は行をまたいで固定添字で参照します。列操作は「行ごとに長さが十分か」チェックを入れると安全です。
2次元の出力整形
int[][] m = {{1,2,3},{4,5,6}};
for (int r = 0; r < m.length; r++) {
for (int c = 0; c < m[r].length; c++) {
System.out.printf("%3d", m[r][c]);
}
System.out.println();
}
Java「行末で改行」などの整形は、外側ループの末尾に配置すると分かりやすくなります。
よくある落とし穴と対策
列数の決め打ちによる範囲外
長方形だと思い込んで m[0].length を使わず固定値にすると、行によっては足りない列にアクセスして例外になります。必ず「その行の length」を参照しましょう。
添字の逆・順番取り違え
m[row][col] の順序を誤ると意図しない値になります。変数名を r/c や row/col にして、コードから意味が伝わるようにしましょう。
浅いコピーと共有の誤解
int[][] m = {{1,2},{3,4}};
int[][] copy = m.clone(); // 外側だけの浅いコピー(内側配列は共有)
copy[0][0] = 99;
System.out.println(m[0][0]); // 99(共有のため影響)
Java完全な独立コピーが必要なら、行ごとに Arrays.copyOf を使って「内側配列もコピー」します。
int[][] deep = new int[m.length][];
for (int r = 0; r < m.length; r++) {
deep[r] = java.util.Arrays.copyOf(m[r], m[r].length);
}
Java実用例で身につける
例 1: 2次元の合計・列ごとの合計
public class Sum2D {
public static void main(String[] args) {
int[][] m = {{1,2,3},{4,5,6}};
int total = 0;
for (int r = 0; r < m.length; r++) {
for (int c = 0; c < m[r].length; c++) {
total += m[r][c];
}
}
int col2 = 0;
for (int r = 0; r < m.length; r++) col2 += m[r][1];
System.out.printf("total=%d, col2=%d%n", total, col2);
}
}
Java例 2: ジャグ配列で行ごとに違う長さを持つ
public class Jagged {
public static void main(String[] args) {
int[][] jag = new int[3][];
jag[0] = new int[] {1};
jag[1] = new int[] {2, 3};
jag[2] = new int[] {4, 5, 6};
for (int r = 0; r < jag.length; r++) {
for (int c = 0; c < jag[r].length; c++) {
System.out.printf("[%d,%d]=%d%n", r, c, jag[r][c]);
}
}
}
}
Java例 3: 行のスワップ(入れ替え)
public class RowSwap {
public static void main(String[] args) {
int[][] m = {{1,2,3},{4,5,6}};
int[] tmp = m[0];
m[0] = m[1];
m[1] = tmp;
System.out.println(java.util.Arrays.deepToString(m)); // [[4, 5, 6], [1, 2, 3]]
}
}
Java設計の指針(重要部分のまとめ)
- 2次元配列は「配列の配列」。行数は
m.length、列数は各行のm[r].lengthを参照する。 - 走査は「未満(
<)」で境界を守る。最後はlength - 1を使う。 - ジャグ配列では常に「その行の長さ」を見る。長方形なら両次元の長さを固定で生成。
- 参照型は初期値が
null。使う前に必ず値を入れるか、null チェックを行う。 - 完全コピーは行ごとの
Arrays.copyOfを使って「内側もコピー」。cloneは外側だけの浅いコピー。
