CodeGym /コース /C# SELF /ジャグ配列 (Jagged Arrays) in C#

ジャグ配列 (Jagged Arrays) in C#

C# SELF
レベル 7 , レッスン 5
使用可能

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) 行ごとに違う場合あり
インデックス指定
[i, j]
[i][j]
柔軟性 低い 高い
用途 テーブル、数学 不均一なデータ:
学生ごとに違う数の成績リスト、三角形など

ビジュアルで比較: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. 配列の配列の仕組み

さて、実際に配列の配列がどうなってるか見てみよう。準備はいい?

普通の配列だと「配列変数は要素を入れるコンテナへの参照を持つ」って感じ。でもジャグ配列だとちょっとややこしい:配列の配列変数は「サブ配列への参照を持つコンテナ」への参照を持ってる。図で見た方が分かりやすいね:

How two-dimensional arrays work

左側 は「配列の配列変数」で、「配列のコンテナオブジェクト」への参照を持ってる。真ん中 は「配列のコンテナオブジェクト」で、その中のセルには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] じゃないよ。
2
タスク
C# SELF, レベル 7, レッスン 5
ロック未解除
シンプルなジャグ配列の宣言と初期化
シンプルなジャグ配列の宣言と初期化
2
タスク
C# SELF, レベル 7, レッスン 5
ロック未解除
ジャグ配列の中で最大値を見つけよう
ジャグ配列の中で最大値を見つけよう
1
アンケート/クイズ
配列、レベル 7、レッスン 5
使用不可
配列
配列の基本
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION