1. ジャグ配列と2次元配列の違い
ついに来たね、みんながよく 「配列の配列」 とか 「ジャグ配列」 って呼ぶやつ — 英語だと jagged arrays だよ。2次元配列と違って、ジャグ配列はカラムごとに長さがバラバラでもOK。イメージとしては、建物の集合体があって、それぞれの建物に部屋数が違う感じ — 1つの建物は5部屋、別のは20部屋、もう1つは1部屋だけ、みたいな。
ジャグ配列 っていうのは、各要素自体が配列になってる配列のこと。しかも中の配列(「サブ配列」とも呼ぶ)は長さがバラバラでも大丈夫。
一番の違いはコレ:
- 2次元配列 だと、どの「行」も「列」も要素数は同じ。例:int[,] grid = new int[3, 5]; — いつも3行×5列。
- ジャグ配列 だと、各行の長さがバラバラ!例:int[][] jagged = new int[3][]; — で、各行(サブ配列)はあとで好きに初期化できる。
見た目はこんな感じ:
| 2次元配列 | ジャグ配列 | |
|---|---|---|
| 要素数 | 固定(例:3x5) | 行ごとに違う場合あり |
| インデックス指定 | |
|
| 柔軟性 | 低い | 高い |
| 用途 | テーブル、数学 | 不均一なデータ: 学生ごとに違う数の成績リスト、三角形など |
ビジュアルで比較:2次元配列 vs ジャグ配列
2次元配列 (3x3):
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ 9 │
└───┴───┴───┘
ジャグ配列 (長さバラバラ):
┌───┬───┐
│ 1 │ 2 │
├───┼───┼───┬───┐
│ 3 │ 4 │ 5 │ 6 │
├───┼───┴───┴───┘
│ 7 │
└───┘
2. ジャグ配列の宣言と初期化の構文
ジャグ配列の宣言は、今までの配列とそんなに変わらないよ!二重の角カッコにビビらなくてOK:
int[][] jaggedArray = new int[3][];
これは「3つの要素を持つ配列で、各要素もint型の配列」って意味。でもこの時点では中の配列はまだ作られてない!もっと分かりやすく順番に見てみよう。
ジャグ配列のステップごとの初期化
ステップ1 — メイン(外側)配列の作成:
int[][] jaggedArray = new int[3][];
これで3つの「行」ができたけど、全部まだ null だよ。
ステップ2 — 中の配列(サブ配列)の作成と初期化:
例えば、1行目は長さ2、2行目は4、3行目は3にしてみる:
jaggedArray[0] = new int[2]; // 1行目は2要素
jaggedArray[1] = new int[4]; // 2行目は4要素
jaggedArray[2] = new int[3]; // 3行目は3要素
ステップ3 — 値を入れる:
中の配列は普通の配列と同じ!例えば:
jaggedArray[0][0] = 1;
jaggedArray[0][1] = 2;
jaggedArray[1][0] = 3;
jaggedArray[1][1] = 4;
jaggedArray[1][2] = 5;
jaggedArray[1][3] = 6;
jaggedArray[2][0] = 7;
jaggedArray[2][1] = 8;
jaggedArray[2][2] = 9;
ジャグ配列の簡単な初期化
もし値が最初から分かってるなら、一気に作って初期化もできる:
int[][] jaggedArray = new int[][]
{
new int[] { 1, 2 },
new int[] { 3, 4, 5, 6 },
new int[] { 7, 8, 9 }
};
中の配列の型を省略してもっと短くもできる:
int[][] jaggedArray = {
new[] { 1, 2 },
new[] { 3, 4, 5, 6 },
new[] { 7, 8, 9 }
};
3. ジャグ配列のループと使い方
ジャグ配列のループも2次元配列とそんなに変わらないけど、外側のループが行、中のループがその行の要素(長さバラバラ)を回る感じ:
for (int i = 0; i < jaggedArray.Length; i++)
{
Console.WriteLine($"行 {i}:");
for (int j = 0; j < jaggedArray[i].Length; j++)
{
Console.Write($"{jaggedArray[i][j]} ");
}
Console.WriteLine();
}
画面の出力例:
行 0:
1 2
行 1:
3 4 5 6
行 2:
7 8 9
foreach を使えばインデックス気にしなくていい:
foreach (int[] row in jaggedArray)
{
foreach (int value in row)
{
Console.Write($"{value} ");
}
Console.WriteLine();
}
4. 配列の配列の仕組み
さて、実際に配列の配列がどうなってるか見てみよう。準備はいい?
普通の配列だと「配列変数は要素を入れるコンテナへの参照を持つ」って感じ。でもジャグ配列だとちょっとややこしい:配列の配列変数は「サブ配列への参照を持つコンテナ」への参照を持ってる。図で見た方が分かりやすいね:
左側 は「配列の配列変数」で、「配列のコンテナオブジェクト」への参照を持ってる。真ん中 は「配列のコンテナオブジェクト」で、その中のセルには1次元配列(ジャグ配列の行)への参照が入ってる。そして 右側 には4つの1次元配列 — これがジャグ配列の行だね。
これがジャグ配列の本当の姿。こういう仕組みだからC#プログラマーにはいくつかメリットがあるんだ:
まず一つ目、「コンテナのコンテナ」が「行配列への参照」を持ってるから、行の入れ替えがめっちゃ簡単。コンテナのコンテナにアクセスするにはインデックス1個でOK。例:int[][] data = new int[2][];
data[0] = new int[5]; // 1行目は5要素の配列
data[1] = new int[5]; // 2行目も5要素の配列
int[] row1 = data[0];
int[] row2 = data[1];
こんな感じで行を入れ替えられる:
// 大事なデータのマトリックス
int[][] matrix = {
new int[] {1, 2, 3, 4, 5},
new int[] {5, 4, 3, 2, 1}
};
int[] tmp = matrix[0];
matrix[0] = matrix[1];
matrix[1] = tmp;
2次元配列のセルにアクセスする時、配列名の後にインデックス1個だけ書くと、「コンテナのコンテナ」にアクセスすることになるよ。その中には普通の1次元配列への参照が入ってる。
5. ジャグ配列のよくある使い方
どんな時にジャグ配列が2次元配列より便利?
- ユーザーごとに違う数のデータ(科目ごとの成績、購入履歴、コメントなど)を保存したい時。
- データが三角形や階段状の構造(ピラミッド、パスカルの三角形など)を持ってる時。
- メモリを節約したい時:2次元配列だと全部の行が同じ長さだけど、ジャグ配列なら必要な分だけ。
実例:学生の成績マネージャー
じゃあ、ちょっとプロジェクトを拡張してみよう!学生ごとに科目の成績数が違う場合を考える。例えば、課題をたくさん出す人もいれば、少ない人もいる。こんな時はジャグ配列がピッタリ。
例えば、3人の学生がいて、数学の課題ごとの成績はこんな感じ:
| 学生 | 成績 |
|---|---|
| 0 | 5, 4 |
| 1 | 3, 4, 4 |
| 2 | 5 |
こんな配列を宣言する:
int[][] studentMarks = new int[3][];
studentMarks[0] = new int[] { 5, 4 }; // 1人目の学生 - 2つの成績
studentMarks[1] = new int[] { 3, 4, 4 }; // 2人目の学生 - 3つの成績
studentMarks[2] = new int[] { 5 }; // 3人目の学生 - 1つの成績
各学生の成績を出力してみる:
for (int i = 0; i < studentMarks.Length; i++)
{
Console.Write($"学生 {i}: ");
for (int j = 0; j < studentMarks[i].Length; j++)
{
Console.Write(studentMarks[i][j] + " ");
}
Console.WriteLine();
}
他の型とのジャグ配列の使い方
ジャグ配列は何でもOK:文字列の配列、配列の配列の配列(さらに深く!)、自作のオブジェクトでもいける。
例:文字列の配列
string[][] groups = new string[][]
{
new string[] { "イワン", "ピョートル" },
new string[] { "マリヤ", "アレクセイ", "セルゲイ" },
new string[] { "ヴァシリサ" }
};
6. 特徴とよくあるミス
ジャグ配列は柔軟だけど、落とし穴もあるから注意!
- 中の配列を初期化してないと(jaggedArray[1] = ... みたいに)、アクセスしようとした時 NullReferenceException になる。必ず各サブ配列を初期化しよう!
- 全部の行(サブ配列)が同じ長さじゃない。2番目のインデックスを固定で使うと範囲外になることも。
- 2次元配列と混同しないで!インデックスは array[i][j] で、array[i, j] じゃないよ。
GO TO FULL VERSION