.NET Frameworkでは単純な配列(1次元配列)だけでなく、2次元以上の多次元配列も扱うことが出来ます。 また、矩形の構造を持った多次元配列に加え、ジャグ配列と呼ばれる配列が入れ子になった構造の配列も扱うことが出来ます。

§1 多次元配列とジャグ配列

多次元配列は通常の配列(1次元配列)の次元を拡張したもので、マトリックスやグリッドの様な矩形の構造を持ったデータ構造であることから、矩形配列とも呼ばれます。 一方ジャグ配列は、配列を格納することが出来る配列(配列の配列)であり、その事から多段配列とも呼ばれます。

ジャグ配列は多次元配列とは異なり、異なる要素数の配列を格納することが出来ます。 例えば、2次元配列が持ちうる要素数は1次元目の長さがn、2次元目の長さがm個とすると計n×m個となり、常に次元ごとの長さの積が全要素数となりますが、2段のジャグ配列ではa個の配列+b個の配列+c個の配列…とジャグ配列内に格納されている配列の要素数の和となります。 ジャグ配列は、矩形である多次元配列とは異なり構造がギザギザ(jagged)になることが、その名前の由来となっています。

2次元配列と2段のジャグ配列を宣言する例
// 3×4個の要素を持つ2次元配列
int[,] matrix = {
  {0, 1, 2, 3},
  {4, 5, 6, 7},
  {8, 9, 10, 11},
};

// 1段目の長さが3、2段目の長さが各々3, 2, 4の2段のジャグ配列
int[][] jagged = {
  new int[] {0, 1, 2},
  new int[] {3, 4},
  new int[] {5, 6, 7, 8},
};

例として2次元配列と2段のジャグ配列を図式化すると次のようになります。 比較のために通常の配列(1次元配列)も併記します。 それぞれ4行×5列の2次元配列と1段目が4個の2段のジャグ配列で、1次元目・1段目を縦方向、2次元目・2段目を横方向で表しています。

1次元配列のイメージ
[0]
[1]
[2]
[3]
2次元配列のイメージ
[0, 0] [0, 1] [0, 2] [0, 3] [0, 4]
[1, 0] [1, 1] [1, 2] [1, 3] [1, 4]
[2, 0] [2, 1] [2, 2] [2, 3] [2, 4]
[3, 0] [3, 1] [3, 2] [3, 3] [3, 4]
2段のジャグ配列のイメージ
[0] [0][1]
[1] [0][1][2][3][4]
[2] [0][1][2]
[3] [0][1][2][3]

多次元配列は行列の演算や表計算のセル表現のなどの用途で良く使われますが、ジャグ配列の用途がイメージしづらければ、カレンダーを想像するとよいでしょう。 カレンダーでは月ごとに日数が異なりますが、これを表現するのにジャグ配列が使えます。 全12ヶ月をジャグ配列の1段目、各月の日数に応じたデータをジャグ配列の2段目で表現出来ます。

2段のジャグ配列で表現したカレンダー
1月 1月1日1月2日1月28日1月29日1月30日1月31日
2月 2月1日2月2日2月28日
3月 3月1日3月2日3月28日3月29日3月30日3月31日
4月 4月1日4月2日4月28日4月29日4月30日
5月 5月1日5月2日5月28日5月29日5月30日5月31日
:
:


§2 多次元配列 (矩形配列)

§2.1 宣言・作成・要素の参照

早速、2次元配列を宣言・作成し要素を参照する例について見てみます。 次の例では1次元目の長さが3、2次元目の長さが4の計3×4個の要素を持つ2次元配列を作成しています。

2次元配列の作成と要素の参照
using System;

class Sample {
  static void Main()
  {
    // 3×4個の要素を持つ2次元配列を作成
    int[,] matrix = new int[3, 4];

    // 1次元目のインデックスが0、2次元目のインデックスが0の要素(最初の要素)に値を設定
    matrix[0, 0] = 0;

    // 1次元目のインデックスが2、2次元目のインデックスが3の要素(最後の要素)に値を設定
    matrix[2, 3] = 11;
  }
}

2次元配列を宣言する場合は、次元毎に要素数(またはインデックスの最大値)をカンマで区切って記述します。 2次元配列内の各要素を参照する場合も同様にインデックスをカンマで区切って記述します。 上記のコードで作成される2次元配列を図式化すると次のようになります。 1次元目を縦方向、2次元目を横方向で表しています。

長さが3×4の2次元配列のイメージ
matrix[0, 0] matrix[0, 1] matrix[0, 2] matrix[0, 3]
matrix[1, 0] matrix[1, 1] matrix[1, 2] matrix[1, 3]
matrix[2, 0] matrix[2, 1] matrix[2, 2] matrix[2, 3]

3次元以上の配列を宣言・作成する場合も同様に、1次元目, 2次元目, 3次元目, … と次元の数だけカンマで区切って長さを指定します。

3次元配列・4次元配列の作成と要素の参照
using System;

class Sample {
  static void Main()
  {
    // 4×2×3個の要素を持つ3次元配列
    int[,,] cube = new int[4, 2, 3];

    cube[0, 1, 2] = 5;

    // 2×2×2×2個の要素を持つ4次元配列を作成
    int[,,,] tesseract = new int[2, 2, 2, 2];

    tesseract[1, 1, 0, 1] = 16;
  }
}

宣言されているものと異なる次元の配列を代入しようとするとコンパイル時にエラーとなります。

次元の異なる配列を代入しようとした場合
using System;

class Sample {
  static void Main()
  {
    // 3次元配列
    int[,,] cube = new int[4, 2, 3];

    // 2次元配列を格納する変数
    int[,] matrix;

    // sample.cs(13,14): error CS0029: 型 'int[*,*,*]' を型 'int[*,*]' に暗黙的に変換できません。
    matrix = cube;
  }
}

§2.2 初期化

多次元配列の場合でも配列初期化子を使って初期化することが出来ます。 配列初期化子を使って初期化する場合は、1つの次元を数列とみなして1次元配列と同様の記述で表現し、それらを入れ子にすることで複数の次元を表現します。 次の例では先ほどと同様の、1次元目の長さが3、2次元目の長さが4の計3×4個の要素を持つ2次元配列を作成すると同時に初期化を行っています。

配列初期化子を使って2次元配列の初期化する
using System;

class Sample {
  static void Main()
  {
    // 3×4個の要素を持つ2次元配列を作成
    int[,] matrix = {
      {0, 1, 2, 3},
      {4, 5, 6, 7},
      {8, 9, 10, 11},
    };

    Console.WriteLine("matrix[0, 0] = {0}", matrix[0, 0]);
    Console.WriteLine("matrix[0, 1] = {0}", matrix[0, 1]);
    Console.WriteLine("matrix[1, 0] = {0}", matrix[1, 0]);
    Console.WriteLine("matrix[2, 3] = {0}", matrix[2, 3]);
  }
}
実行結果
matrix[0, 0] = 0
matrix[0, 1] = 1
matrix[1, 0] = 4
matrix[2, 3] = 11

このコードで作成・初期化される2次元配列を図式化すると次のようになります。

初期化後に2次元配列へ格納される値のイメージ
matrix[m, n] [m, 0] [m, 1] [m, 2] [m, 3]
[0, n] 0 1 2 3
[1, n] 4 5 6 7
[2, n] 8 9 10 11

3次元以上の多次元配列も同様にして初期化することが出来ます。 2次元目は入れ子の2段目、3次元目は入れ子の3段目と、より高い次元の数列を入れ子の内側に記述します。 次元が高くなる分、記述も複雑になるので注意してください。

配列初期化子を使って3次元配列の初期化する
using System;

class Sample {
  static void Main()
  {
    // 4×2×3個の要素を持つ3次元配列
    int[,,] cube = {
      {  { 0,  1,  2},  { 3,  4,  5}  },
      {  { 6,  7,  8},  { 9, 10, 11}  },
      {  {12, 13, 14},  {15, 16, 17}  },
      {  {18, 19, 20},  {21, 22, 23}  },
    };

    Console.WriteLine("cube[0, 0, 0] = {0}", cube[0, 0, 0]);
    Console.WriteLine("cube[0, 0, 1] = {0}", cube[0, 0, 1]);
    Console.WriteLine("cube[0, 1, 0] = {0}", cube[0, 1, 0]);
    Console.WriteLine("cube[1, 0, 0] = {0}", cube[1, 0, 0]);
    Console.WriteLine("cube[3, 1, 2] = {0}", cube[3, 1, 2]);
  }
}
実行結果
cube[0, 0, 0] = 0
cube[0, 0, 1] = 1
cube[0, 1, 0] = 3
cube[1, 0, 0] = 6
cube[3, 1, 2] = 23
配列初期化子を使って4次元配列の初期化する
using System;

class Sample {
  static void Main()
  {
    // 2×2×2×2個の要素を持つ4次元配列
    int[,,,] tesseract = {
      {  {  {0, 1},  { 2,  3}  },  {  { 4,  5},  { 6,  7}  }  },
      {  {  {8, 9},  {10, 11}  },  {  {12, 13},  {14, 15}  }  },
    };

    Console.WriteLine("tesseract[0, 0, 0, 0] = {0}", tesseract[0, 0, 0, 0]);
    Console.WriteLine("tesseract[0, 0, 0, 1] = {0}", tesseract[0, 0, 0, 1]);
    Console.WriteLine("tesseract[0, 0, 1, 0] = {0}", tesseract[0, 0, 1, 0]);
    Console.WriteLine("tesseract[0, 1, 0, 0] = {0}", tesseract[0, 1, 0, 0]);
    Console.WriteLine("tesseract[1, 0, 0, 0] = {0}", tesseract[1, 0, 0, 0]);
    Console.WriteLine("tesseract[1, 1, 1, 1] = {0}", tesseract[1, 1, 1, 1]);
  }
}
実行結果
tesseract[0, 0, 0, 0] = 0
tesseract[0, 0, 0, 1] = 1
tesseract[0, 0, 1, 0] = 2
tesseract[0, 1, 0, 0] = 4
tesseract[1, 0, 0, 0] = 8
tesseract[1, 1, 1, 1] = 15

§2.3 長さ・次元数の取得

多次元配列でもLengthプロパティを参照することが出来ますが、返される値は多次元配列の全要素数となります。 次元毎の長さを取得したい場合は、GetLengthメソッドを呼び出します。 取得したい次元の番号を指定してGetLengthメソッドを呼び出すことで、多次元配列におけるその次元の長さが返されます。 GetLengthメソッドでは、最初の次元は0、その次の次元は1として扱われるため、例えば3次元配列の場合は0~2の値を次元の番号として指定します。 1次元配列に対してもGetLengthメソッドを呼び出すことは出来ますが、返される結果はLengthプロパティと同じです。

また、Rankプロパティを参照することで、多次元配列の次元数・次元の高さを取得できます。 1次元配列では1、2次元配列では2が返されます。

多次元配列の全要素数・次元数・次元毎の長さを取得する
using System;

class Sample {
  static void Main()
  {
    // 5個の要素を持つ1次元配列
    int[] arr = new int[5];

    Console.WriteLine("arr.Length = {0}", arr.Length);
    Console.WriteLine("arr.Rank = {0}", arr.Rank);
    Console.WriteLine("arr = {0}", arr.GetLength(0));
    Console.WriteLine();

    // 3×4個の要素を持つ2次元配列
    int[,] matrix = new int[3, 4];

    Console.WriteLine("matrix.Length = {0}", matrix.Length);
    Console.WriteLine("matrix.Rank = {0}", matrix.Rank);
    Console.WriteLine("matrix = {0}×{1}", matrix.GetLength(0), matrix.GetLength(1));
    Console.WriteLine();

    // 4×2×3個の要素を持つ3次元配列
    int[,,] cube = new int[4, 2, 3];

    Console.WriteLine("cube.Length = {0}", cube.Length);
    Console.WriteLine("cube.Rank = {0}", cube.Rank);
    Console.WriteLine("cube = {0}×{1}×{2}", cube.GetLength(0), cube.GetLength(1), cube.GetLength(2));
    Console.WriteLine();

    // 2×2×2×2個の要素を持つ4次元配列
    int[,,,] tesseract = new int[2, 2, 2, 2];

    Console.WriteLine("tesseract.Length = {0}", tesseract.Length);
    Console.WriteLine("tesseract.Rank = {0}", tesseract.Rank);
    Console.WriteLine("tesseract = {0}×{1}×{2}×{3}", tesseract.GetLength(0), tesseract.GetLength(1), tesseract.GetLength(2), tesseract.GetLength(3));
  }
}
実行結果
arr.Length = 5
arr.Rank = 1
arr = 5

matrix.Length = 12
matrix.Rank = 2
matrix = 3×4

cube.Length = 24
cube.Rank = 3
cube = 4×2×3

tesseract.Length = 16
tesseract.Rank = 4
tesseract = 2×2×2×2

§2.4 要素の列挙

多次元配列も1次元配列と同様for文・foreach文で列挙することが出来ます。 ただ、foreach文で列挙する場合は注意が必要で、行や列ごとといった部分配列が列挙されるのではなく、次元が平坦化された1次元の数列として列挙されます。 この際、多段配列内の各要素は、より高い次元にあるインデックスの小さい要素から順に列挙されます。 多次元配列から行や列ごとに要素を抽出したい場合は、foreach文ではなくfor文でインデックスを指定して列挙する必要があります。

2次元配列の列挙
using System;

class Sample {
  static void Main()
  {
    // 3×4個の要素を持つ2次元配列
    int[,] matrix = {
      {0, 1, 2, 3},
      {4, 5, 6, 7},
      {8, 9, 10, 11},
    };

    // for文を使って列挙
    for (int d1 = 0; d1 < matrix.GetLength(0); d1++) {
      Console.Write("( ");
      for (int d2 = 0; d2 < matrix.GetLength(1); d2++) {
        Console.Write("{0}, ", matrix[d1, d2]);
      }
      Console.WriteLine("), ");
    }
    Console.WriteLine();

    // foreach文を使って列挙
    foreach (int elem in matrix) {
      Console.Write("{0}, ", elem);
    }
    Console.WriteLine();
  }
}
実行結果
( 0, 1, 2, 3, ), 
( 4, 5, 6, 7, ), 
( 8, 9, 10, 11, ), 

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 
3次元配列の列挙
using System;

class Sample {
  static void Main()
  {
    // 4×2×3個の要素を持つ3次元配列
    int[,,] cube = {
      {  { 0,  1,  2},  { 3,  4,  5}  },
      {  { 6,  7,  8},  { 9, 10, 11}  },
      {  {12, 13, 14},  {15, 16, 17}  },
      {  {18, 19, 20},  {21, 22, 23}  },
    };

    // for文を使って列挙
    for (int d1 = 0; d1 < cube.GetLength(0); d1++) {
      Console.Write("{ ");
      for (int d2 = 0; d2 < cube.GetLength(1); d2++) {
        Console.Write("( ");
        for (int d3 = 0; d3 < cube.GetLength(2); d3++) {
          Console.Write("{0}, ", cube[d1, d2, d3]);
        }
        Console.Write("), ");
      }
      Console.WriteLine("}, ");
    }
    Console.WriteLine();

    // foreach文を使って列挙
    foreach (int elem in cube) {
      Console.Write("{0}, ", elem);
    }
    Console.WriteLine();
  }
}
実行結果
{ ( 0, 1, 2, ), ( 3, 4, 5, ), }, 
{ ( 6, 7, 8, ), ( 9, 10, 11, ), }, 
{ ( 12, 13, 14, ), ( 15, 16, 17, ), }, 
{ ( 18, 19, 20, ), ( 21, 22, 23, ), }, 

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 
4次元配列の列挙
using System;

class Sample {
  static void Main()
  {
    // 2×2×2×2個の要素を持つ4次元配列
    int[,,,] tesseract = {
      {  {  {0, 1},  { 2,  3}  },  {  { 4,  5},  { 6,  7}  }  },
      {  {  {8, 9},  {10, 11}  },  {  {12, 13},  {14, 15}  }  },
    };

    // for文を使って列挙
    for (int d1 = 0; d1 < tesseract.GetLength(0); d1++) {
      Console.Write("[ ");
      for (int d2 = 0; d2 < tesseract.GetLength(1); d2++) {
        Console.Write("{ ");
        for (int d3 = 0; d3 < tesseract.GetLength(2); d3++) {
          Console.Write("( ");
          for (int d4 = 0; d4 < tesseract.GetLength(3); d4++) {
            Console.Write("{0}, ", tesseract[d1, d2, d3, d4]);
          }
          Console.Write("), ");
        }
        Console.Write("}, ");
      }
      Console.WriteLine("], ");
    }
    Console.WriteLine();

    // foreach文を使って列挙
    foreach (int elem in tesseract) {
      Console.Write("{0}, ", elem);
    }
    Console.WriteLine();
  }
}
実行結果
[ { ( 0, 1, ), ( 2, 3, ), }, { ( 4, 5, ), ( 6, 7, ), }, ], 
[ { ( 8, 9, ), ( 10, 11, ), }, { ( 12, 13, ), ( 14, 15, ), }, ], 

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 

§2.5 任意の次元数の配列を扱う例

次の例は、Lengthプロパティ・Rankプロパティ・GetLengthメソッドなどと組み合わせて多次元配列を扱う例です。 このコードでは、任意の次元数・要素数の多次元配列を引数にとり、その多次元配列の次元・長さ・要素数・格納されている全要素を表示するメソッドを実装しています。 どのような配列もArrayクラスから暗黙的に派生しているので、引数の型をArrayとすることで任意の配列を受け取れるようにしています。 また、配列内の全要素はforeach文と同じ順で列挙されるようにしています。

なお、このコードで使用しているGetValueメソッドは、引数としてインデックスを渡すことにより、配列内の該当するインデックスの値を取得することが出来るメソッドです。 詳しくは配列操作 §.配列の作成・要素の取得と設定 (CreateInstance, GetValue, SetValue)で解説しています。

任意の次元数・要素数の配列から、配列の次元・長さ・要素数・格納されている全要素を表示する
using System;

class Sample {
  static void Print(Array arr)
  {
    Console.WriteLine("次元: {0}", arr.Rank);

    Console.Write("長さ: ");

    for (int d = 0; d < arr.Rank; d++) {
      if (0 < d)
        Console.Write({0}", arr.GetLength(d));
      else
        Console.Write("{0}", arr.GetLength(d));
    }

    Console.WriteLine();

    Console.WriteLine("要素数: {0}", arr.Length);

    Console.Write("要素: ");

    // 取得したい要素のインデックスを指定するための配列
    int[] indices = new int[arr.Rank];

    for (;;) {
      // [step1] step3でインデックスをインクリメントした結果、各次元の長さに達したかどうか調べる
      for (int d = arr.Rank - 1; 0 <= d; d--) {
        if (arr.GetLength(d) <= indices[d]) {
          // d次元目のインデックスがd次元目の長さを越えている場合
          if (d == 0) {
            // 1次元目(d = 0)の場合は、インクリメントした結果が1次元目の長さに達しているので終了する
            Console.WriteLine();
            return;
          }
          else {
            // d次元目のインデックスを0に戻し、一つ低い次元(d - 1次元)のインデックスをインクリメントする
            indices[d] = 0;
            indices[d - 1]++;
          }
        }
      }

      // [step2] 指定されたインデックスの要素を取得して表示
      Console.Write("{0}, ", arr.GetValue(indices));

      // [step3] もっとも高い次元のインデックスをインクリメント
      indices[arr.Rank - 1]++;
    }
  }

  static void Main()
  {
    // 5個の要素を持つ1次元配列
    int[] arr = {
      0, 1, 2, 3, 4,
    };

    Print(arr);
    Console.WriteLine();

    // 3×4個の要素を持つ2次元配列
    int[,] matrix = {
      {0, 1, 2, 3},
      {4, 5, 6, 7},
      {8, 9, 10, 11},
    };

    Print(matrix);
    Console.WriteLine();

    // 4×2×3個の要素を持つ3次元配列
    int[,,] cube = {
      {  { 0,  1,  2},  { 3,  4,  5}  },
      {  { 6,  7,  8},  { 9, 10, 11}  },
      {  {12, 13, 14},  {15, 16, 17}  },
      {  {18, 19, 20},  {21, 22, 23}  },
    };

    Print(cube);
    Console.WriteLine();

    // 2×2×2×2個の要素を持つ4次元配列
    int[,,,] tesseract = {
      {  {  {0, 1},  { 2,  3}  },  {  { 4,  5},  { 6,  7}  }  },
      {  {  {8, 9},  {10, 11}  },  {  {12, 13},  {14, 15}  }  },
    };

    Print(tesseract);
    Console.WriteLine();

    // 1×1×1×1×1個の要素を持つ5次元配列
    Print(new int[1, 1, 1, 1, 1]);
    Console.WriteLine();

    // 1×0×1×0×1×0個の要素を持つ6次元配列
    Print(new int[1, 0, 1, 0, 1, 0]);
    Console.WriteLine();
  }
}
実行結果
次元: 1
長さ: 5
要素数: 5
要素: 0, 1, 2, 3, 4, 

次元: 2
長さ: 3×4
要素数: 12
要素: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 

次元: 3
長さ: 4×2×3
要素数: 24
要素: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 

次元: 4
長さ: 2×2×2×2
要素数: 16
要素: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 

次元: 5
長さ: 1×1×1×1×1
要素数: 1
要素: 0, 

次元: 6
長さ: 1×0×1×0×1×0
要素数: 0
要素: 

§3 ジャグ配列 (多段配列)

§3.1 宣言・作成・要素の参照

2段のジャグ配列を宣言・作成し、要素を参照する例について見てみます。 次の例では、1段目の長さが3、2段目の長さが各々3, 2, 4の計9個の要素を持つ2段のジャグ配列を作成しています。

2段のジャグ配列の作成と要素の参照
using System;

class Sample {
  static void Main()
  {
    // 1段目の長さが3である2段のジャグ配列を作成
    int[][] jagged = new int[3][];

    // 作成したジャグ配列の1段目に、2段目となる配列を代入
    jagged[0] = new int[3]; // 1段目の0番目に長さが3の配列を代入
    jagged[1] = new int[2]; // 1段目の1番目に長さが2の配列を代入
    jagged[2] = new int[4]; // 1段目の2番目に長さが4の配列を代入

    // 1段目のインデックスが0、2段目のインデックスが0の要素に値を設定
    jagged[0][0] = 0;

    // 1段目のインデックスが2、2段目のインデックスが3の要素に値を設定
    jagged[2][3] = 8;
  }
}

2段のジャグ配列を宣言する場合は、1段目の要素数(またはインデックスの最大値)と、要素数を空にした2段目を括弧を連ねて記述します。 2段目に要素数を指定することは出来ません。 このように宣言されたジャグ配列では、2段目の配列はまだ作成されておらずヌル参照(null/Nothing)となっています。 そこからさらに2段目となる配列を1段目に代入していくことで、2段のジャグ配列を構成することが出来ます。

2段のジャグ配列内の各要素を参照する場合は、1段目と2段目のインデックスを括弧を連ねて記述します。 上記のコードで作成される2段のジャグ配列を図式化すると次のようになります。 1段目を縦方向、2段目を横方向で表しています。

1段目の長さが3の2段のジャグ配列のイメージ
jagged[0] → jagged[0][0]jagged[0][1]jagged[0][2]
jagged[1] → jagged[1][0]jagged[1][1]
jagged[2] → jagged[2][0]jagged[2][1]jagged[2][2]jagged[2][3]

3段以上のジャグ配列を宣言・作成する場合も同様に、まず1段目の要素数のみを指定し、段数分だけ括弧を連ねて記述します。 そこから2段目となる配列の代入、さらにそこへ3段目となる配列を代入…と繰り替えしていきます。 要素を参照する場合も同様に、[1段目][2段目][3段目][…]と段数分だけ括弧を連ねて記述します。

3段のジャグ配列の作成と要素の参照
using System;

class Sample {
  static void Main()
  {
    // 1段目の長さが3である3段のジャグ配列を作成
    int[][][] doubleNested = new int[3][][];

    // 1段目に2段目となる配列を代入
    doubleNested[2] = new int[2][]; // 1段目の2番目に長さが2で2段のジャグ配列を代入

    // 2段目に3段目となる配列を代入
    doubleNested[2][0] = new int[4]; // 1段目の0番目、2段目の0番目に長さが4の配列を代入

    // 3段目の要素に値を代入
    doubleNested[2][0][3] = 5;
  }
}
4段のジャグ配列の作成と要素の参照
using System;

class Sample {
  static void Main()
  {
    // 1段目の長さが2である4段のジャグ配列を作成
    int[][][][] tripleNested = new int[2][][][];

    // 1段目に2段目となる配列を代入
    tripleNested[1] = new int[1][][]; // 1段目の1番目に長さが1で3段のジャグ配列を代入

    // 2段目に3段目となる配列を代入
    tripleNested[1][0] = new int[3][]; // 1段目の1番目、2段目の0番目に長さが3で2段のジャグ配列を代入

    // 3段目に4段目となる配列を代入
    tripleNested[1][0][2] = new int[2]; // 1段目の1番目、2段目の0番目、3段目の2番目に長さが2の配列を代入

    // 4段目の要素に値を代入
    tripleNested[1][0][2][1] = 16;
  }
}

ジャグ配列は配列の配列であることから、宣言等の記述を次のように読み替えることも出来ます。

ジャグ配列の記述の読み替え
配列 記述 読み替え
2段のジャグ配列 int[][] int 型の 配列 [] の配列 []
もしくは
「1段のジャグ配列 int[] に 配列 [] を含めるようにしたもの」
3段のジャグ配列 int[][][] int 型の 配列 [] の配列 [] の配列 []
もしくは
「2段のジャグ配列 int[][] に 配列 [] を含めるようにしたもの」
4段のジャグ配列 int[][][][] int 型の 配列 [] の配列 [] の配列 [] の配列 []
もしくは
「3段のジャグ配列 int[][][] に 配列 [] を含めるようにしたもの」

宣言されているものと異なる段数の配列を代入しようとするとコンパイル時にエラーとなります。

段数の異なるジャグ配列を代入しようとした場合
using System;

class Sample {
  static void Main()
  {
    // 3段のジャグ配列
    int[][][] doubleNested = new int[3][][];

    // 2段のジャグ配列を格納する変数
    int[][] jagged;

    // sample.cs(13,14): error CS0029: 型 'int[][][]' を型 'int[][]' に暗黙的に変換できません。
    jagged = doubleNested;
  }
}

§3.2 初期化

ジャグ配列の場合でも配列初期化子を使って初期化することが出来ます。 配列初期化子を使って初期化する場合、多次元配列の場合と同様の記述が出来ますが、1段目はnewを省略して記述出来るのに対し、2段目以降はnewを省略して記述することは出来ないという点に注意が必要です。 次の例では先ほどと同様の、1段目の長さが3、2段目の長さが各々3, 2, 4の計9個の要素を持つ2段のジャグ配列を作成すると同時に初期化を行っています。

2段のジャグ配列の初期化
using System;

class Sample {
  static void Main()
  {
    // 1段目の長さが3、2段目の長さが各々3, 2, 4の2段のジャグ配列を作成
    int[][] jagged = {
      new int[] {0, 1, 2},
      new int[] {3, 4},
      new int[] {5, 6, 7, 8},
    };

    Console.WriteLine("jagged[0][0] = {0}", jagged[0][0]);
    Console.WriteLine("jagged[0][1] = {0}", jagged[0][1]);
    Console.WriteLine("jagged[1][0] = {0}", jagged[1][0]);
    Console.WriteLine("jagged[2][3] = {0}", jagged[2][3]);
  }
}
実行結果
jagged[0][0] = 0
jagged[0][1] = 1
jagged[1][0] = 3
jagged[2][3] = 8

このコードで作成・初期化される2段のジャグ配列を図式化すると次のようになります。

初期化後に2段のジャグ配列へ格納される値のイメージ
jagged[0] → 012
jagged[1] → 34
jagged[2] → 5678

3段以上のジャグ配列も同様にして初期化することが出来ます。 配列の中に配列を含めるよう入れ子に記述していくことで多段のジャグ配列を初期化できます。

3段のジャグ配列の初期化
using System;

class Sample {
  static void Main()
  {
    // 3段のジャグ配列
    int[][][] doubleNested = {
      new int[][] {
        new int[] {0, 1, 2},
        new int[] {3, 4},
        new int[] {5, 6, 7, 8},
      },
      new int[][] {
        new int[] {9, 10},
        new int[] {11, 12, 13, 14},
      },
      new int[][] {
        new int[] {15, 16, 17},
        new int[] {18},
        new int[] {19, 20},
        new int[] {21, 22},
      },
    };

    Console.WriteLine("doubleNested[0][0][0] = {0}", doubleNested[0][0][0]);
    Console.WriteLine("doubleNested[0][2][3] = {0}", doubleNested[0][2][3]);
    Console.WriteLine("doubleNested[1][0][0] = {0}", doubleNested[1][0][0]);
    Console.WriteLine("doubleNested[2][3][1] = {0}", doubleNested[2][3][1]);
  }
}
実行結果
doubleNested[0][0][0] = 0
doubleNested[0][2][3] = 8
doubleNested[1][0][0] = 9
doubleNested[2][3][1] = 22

§3.3 長さの取得

多次元配列とは異なり、Lengthプロパティを参照しても全要素を取得することは出来ません。 ジャグ配列の場合、Lengthプロパティは1次元配列と同様、1段目の長さを返します。 また、ジャグ配列では各段で長さが異なるため、GetLengthメソッドを使って2段目以降の長さを取得するといったことも出来ません。

ジャグ配列の長さの取得
using System;

class Sample {
  static void Main()
  {
    // 1段目の長さが3である2段のジャグ配列
    int[][] jagged = new int[3][];

    Console.WriteLine("jagged.Length = {0}", jagged.Length);

    // 1段目の長さが3である3段のジャグ配列
    int[][][] doubleNested = new int[3][][];

    Console.WriteLine("doubleNested.Length = {0}", doubleNested.Length);

    // 1段目の長さが2である4段のジャグ配列
    int[][][][] tripleNested = new int[2][][][];

    Console.WriteLine("tripleNested.Length = {0}", tripleNested.Length);
  }
}
実行結果
jagged.Length = 3
doubleNested.Length = 3
tripleNested.Length = 2

2段目以降の長さを取得する場合は、ジャグ配列内に格納されている配列のLengthプロパティを個別に参照する必要があります。

ジャグ配列の2段目の長さの取得
using System;

class Sample {
  static void Main()
  {
    // 2段のジャグ配列
    int[][] jagged = {
      new int[] {0, 1, 2},
      new int[] {3, 4},
      new int[] {5, 6, 7, 8},
    };

    Console.WriteLine("jagged[0].Length = {0}", jagged[0].Length);
    Console.WriteLine("jagged[1].Length = {0}", jagged[1].Length);
    Console.WriteLine("jagged[2].Length = {0}", jagged[2].Length);
  }
}
実行結果
jagged[0].Length = 3
jagged[1].Length = 2
jagged[2].Length = 4

§3.4 要素の列挙

ジャグ配列も1次元配列と同様for文・foreach文で列挙することが出来ます。 foreach文で列挙する場合は、多次元配列のように配列内の全要素が一つずつ列挙されるのではなく、ジャグ配列の1段目に格納されている配列が列挙されます。

2段のジャグ配列の列挙
using System;

class Sample {
  static void Main()
  {
    // 2段のジャグ配列
    int[][] jagged = {
      new int[] {0, 1, 2},
      new int[] {3, 4},
      new int[] {5, 6, 7, 8},
    };

    // for文を使って列挙
    for (int n1 = 0; n1 < jagged.Length; n1++) {
      Console.Write("( ");
      for (int n2 = 0; n2 < jagged[n1].Length; n2++) {
        Console.Write("{0}, ", jagged[n1][n2]);
      }
      Console.WriteLine("), ");
    }
    Console.WriteLine();

    // foreach文を使って列挙
    foreach (int[] arr in jagged) {
      Console.Write("( ");
      foreach (int elem in arr) {
        Console.Write("{0}, ", elem);
      }
      Console.WriteLine("), ");
    }
    Console.WriteLine();
  }
}
実行結果
( 0, 1, 2, ), 
( 3, 4, ), 
( 5, 6, 7, 8, ), 

( 0, 1, 2, ), 
( 3, 4, ), 
( 5, 6, 7, 8, ), 
3段のジャグ配列の列挙
using System;

class Sample {
  static void Main()
  {
    // 3段のジャグ配列
    int[][][] doubleNested = {
      new int[][] {
        new int[] {0, 1, 2},
        new int[] {3, 4},
        new int[] {5, 6, 7, 8},
      },
      new int[][] {
        new int[] {9, 10},
        new int[] {11, 12, 13, 14},
      },
      new int[][] {
        new int[] {15, 16, 17},
        new int[] {18},
        new int[] {19, 20},
        new int[] {21, 22},
      },
    };

    // for文を使って列挙
    for (int n1 = 0; n1 < doubleNested.Length; n1++) {
      Console.Write("{ ");
      for (int n2 = 0; n2 < doubleNested[n1].Length; n2++) {
        Console.Write("( ");
        for (int n3 = 0; n3 < doubleNested[n1][n2].Length; n3++) {
          Console.Write("{0}, ", doubleNested[n1][n2][n3]);
        }
        Console.Write("), ");
      }
      Console.WriteLine("}, ");
    }
    Console.WriteLine();

    // foreach文を使って列挙
    foreach (int[][] jagged in doubleNested) {
      Console.Write("{ ");
      foreach (int[] arr in jagged) {
        Console.Write("( ");
        foreach (int elem in arr) {
          Console.Write("{0}, ", elem);
        }
        Console.Write("), ");
      }
      Console.WriteLine("}, ");
    }
    Console.WriteLine();
  }
}
実行結果
{ ( 0, 1, 2, ), ( 3, 4, ), ( 5, 6, 7, 8, ), }, 
{ ( 9, 10, ), ( 11, 12, 13, 14, ), }, 
{ ( 15, 16, 17, ), ( 18, ), ( 19, 20, ), ( 21, 22, ), }, 

{ ( 0, 1, 2, ), ( 3, 4, ), ( 5, 6, 7, 8, ), }, 
{ ( 9, 10, ), ( 11, 12, 13, 14, ), }, 
{ ( 15, 16, 17, ), ( 18, ), ( 19, 20, ), ( 21, 22, ), },