.NETでは、単純な配列(1次元配列)だけでなく、矩形の構造を持つ多次元配列(矩形配列)と、配列が入れ子になった構造を持つジャグ配列(多段配列・配列の配列)を扱うことができます。
多次元配列とジャグ配列
多次元配列は通常の配列(1次元配列)の次元を拡張したもので、マトリックスやグリッド、テーブルの様な矩形の構造を持ったデータ構造であることから、矩形配列とも呼ばれます。 (§.多次元配列 (矩形配列))
一方、ジャグ配列は配列を格納することができる配列(配列の配列)であり、その事から多段配列とも呼ばれます。 ジャグ配列は、常に矩形である多次元配列とは異なり構造がギザギザ(jagged)になることが、その名前の由来となっています。 (§.ジャグ配列 (多段配列))
ジャグ配列は多次元配列とは異なり、異なる要素数の配列を格納することができます。 例えば、2次元配列が持ちうる要素数は1次元目の長さがn、2次元目の長さがm個とすると計n×m個となり、常に次元ごとの長さの積が全要素数となります。 一方、2段のジャグ配列ではa個の配列+b個の配列+c個の配列…とジャグ配列内に格納されている配列の要素数の和となります。
上記のコードで表現される配列・2次元配列・2段のジャグ配列を図式化すると次のようになります。
0 | 1 | 2 | 3 |
0 | 1 | 2 | 3 |
4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 |
0 1 2 |
3 4 |
5 6 7 8 |
多次元配列は行列の演算や表計算のセル表現のなどの用途で良く使われますが、ジャグ配列の用途がイメージしづらければ、カレンダーを想像するとよいでしょう。 カレンダーでは月ごとに日数が異なりますが、これを表現するのにジャグ配列が使えます。 全12ヶ月をジャグ配列の1段目、各月の日数に応じたデータをジャグ配列の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次元配列を宣言・作成し要素を参照する例について見てみます。 次の例では1次元目の長さが3、2次元目の長さが4の計3×4個の要素を持つ2次元配列を作成しています。
2次元配列を宣言する場合は、次元毎に要素数(またはインデックスの最大値)をカンマで区切って記述します。 2次元配列内の各要素を参照する場合も同様にインデックスをカンマで区切って記述します。
上記のコードで作成される2次元配列を図式化すると次のようになります。 1次元目を縦方向、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次元目, … と次元の数だけカンマで区切って長さを指定します。
宣言されているものと異なる次元の配列を代入しようとするとコンパイル時にエラーとなります。
初期化 (配列初期化子)
多次元配列の場合でも配列初期化子を使って初期化することができます。 配列初期化子を使って初期化する場合は、1つの次元を数列とみなして1次元配列と同様の記述で表現し、それらを入れ子にすることで複数の次元を表現します。
次の例では、1次元目の長さが3、2次元目の長さが4の計3×4個の要素を持つ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段目と、より高い次元の数列を入れ子の内側に記述します。 次元が高くなる分、記述も複雑になるので注意してください。
次元ごとの長さ・次元数・要素数の取得 (Length/GetLength/Rank)
1次元配列でLengthプロパティを参照すると要素数が取得できます。 一方、多次元配列でLengthプロパティを参照した場合、返される値は多次元配列の全要素数となります。 つまり、3×4個の要素を持つ2次元配列の場合、Lengthプロパティは12を返します。
次元毎の長さを取得したい場合は、GetLengthメソッドを呼び出します。 取得したい次元の番号を指定してGetLengthメソッドを呼び出すことで、多次元配列におけるその次元の長さが返されます。
GetLengthメソッドでは、最初の次元は0、その次の次元は1として扱われるため、例えば3次元配列の場合は0~2の値を次元の番号として指定します。 1次元配列に対してもGetLengthメソッドを呼び出すことはできます。 この場合、返される結果はLengthプロパティと同じです。
また、Rankプロパティを参照することで、多次元配列の次元数(次元の高さ)を取得できます。 1次元配列では1、2次元配列では2が返されます。
要素の列挙
多次元配列も1次元配列と同様for文・foreach文で列挙することができます。 ただ、foreach文で多次元配列を列挙する場合、行や列ごとといった部分配列が列挙されるのではなく、次元が平坦化された1次元の数列として列挙されます。 この際、多段配列内の各要素は、より高い次元にあるインデックスの小さい要素から順に列挙されます。 そのため、多次元配列から行や列ごとに要素を抽出したい場合は、foreach文ではなくfor文でインデックスを指定して列挙する必要があります。
任意の次元数の配列を扱う例
次の例は、Lengthプロパティ・Rankプロパティ・GetLengthメソッドなどと組み合わせて多次元配列を扱う例です。 このコードでは、任意の次元数・要素数の多次元配列を引数にとり、その多次元配列の次元・長さ・要素数・格納されている全要素を表示するメソッドを実装しています。 どのような配列もArrayクラスから暗黙的に派生しているので、引数の型をArrayとすることで任意の配列を受け取れるようにしています。 また、配列内の全要素はforeach文と同じ順で列挙されるようにしています。
なお、このコードで使用しているGetValueメソッドは、引数としてインデックスを渡すことにより、配列内の該当するインデックスの値を取得することができるメソッドです。 詳しくは配列操作 §.配列の作成・要素の取得と設定 (CreateInstance, GetValue, SetValue)で解説しています。
ジャグ配列 (多段配列)
宣言・作成・要素の参照
2段のジャグ配列を宣言・作成し、要素を参照する例について見てみます。 次の例では、1段目の長さが3、2段目の長さが各々3, 2, 4の計9個の要素を持つ2段のジャグ配列を作成しています。
2段のジャグ配列を宣言する場合は、1段目の要素数(またはインデックスの最大値)と、要素数を空にした2段目を括弧を連ねて記述します。 2段目に要素数を指定することはできません。 このように宣言されたジャグ配列では、2段目の配列はまだ作成されておらずヌル参照(null
/Nothing
)となっています。 そこからさらに2段目となる配列を1段目に代入していくことで、2段のジャグ配列を構成することができます。
2段のジャグ配列内の各要素を参照する場合は、1段目と2段目のインデックスを括弧を連ねて記述します。 上記のコードで作成される2段のジャグ配列を図式化すると次のようになります。 1段目を縦方向、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段目][…]
と段数分だけ括弧を連ねて記述します。
ジャグ配列は配列の配列であることから、宣言等の記述を次のように読み替えることもできます。
配列 | 記述 | 読み替え |
---|---|---|
2段のジャグ配列 |
int[][]
|
「int 型の 配列 [] の配列 []」 もしくは 「1段のジャグ配列 int[] に 配列 [] を含めるようにしたもの」 |
3段のジャグ配列 |
int[][][]
|
「int 型の 配列 [] の配列 [] の配列 []」 もしくは 「2段のジャグ配列 int[][] に 配列 [] を含めるようにしたもの」 |
4段のジャグ配列 |
int[][][][]
|
「int 型の 配列 [] の配列 [] の配列 [] の配列 []」 もしくは 「3段のジャグ配列 int[][][] に 配列 [] を含めるようにしたもの」 |
宣言されているものと異なる段数の配列を代入しようとするとコンパイル時にエラーとなります。
初期化 (配列初期化子)
ジャグ配列の場合でも配列初期化子を使って初期化することができます。 配列初期化子を使って初期化する場合、多次元配列の場合と同様の記述ができますが、1段目はnew
を省略して記述できるのに対し、2段目以降はnew
を省略して記述することはできないという点に注意が必要です。
次の例では、1段目の長さが3、2段目の長さが各々3, 2, 4の計9個の要素を持つ2段のジャグ配列を作成すると同時に初期化を行っています。
このコードで作成・初期化される2段のジャグ配列を図式化すると次のようになります。
jagged[0] → 012 |
jagged[1] → 34 |
jagged[2] → 5678 |
3段以上のジャグ配列も同様にして初期化することができます。 配列の中に配列を含めるよう入れ子に記述していくことで多段のジャグ配列を初期化できます。
長さの取得
多次元配列のLengthプロパティとは異なり、ジャグ配列ではLengthプロパティを参照しても全要素を取得することはできません。 また、ジャグ配列では各段で長さが異なる場合があることから、GetLengthメソッドを使って2段目以降の長さを取得するといったこともできません。
ジャグ配列の場合、Lengthプロパティは1次元配列と同様、1段目の長さを返します。
2段目以降の長さを取得する場合は、ジャグ配列内に格納されている配列のLengthプロパティを個別に参照する必要があります。
要素の列挙
ジャグ配列も1次元配列と同様for文・foreach文で列挙することができます。 ただし、foreach文でジャグ配列を列挙する場合は、多次元配列のように配列内の全要素が一つずつ列挙されるのではなく、単にジャグ配列の1段目に格納されている配列が列挙されます。 そのため、ジャグ配列内のすべての要素を列挙したい場合は、for文・foreach文ともに各段それぞれでfor文・foreach文を入れ子にしていくことで列挙する必要があります。
多次元配列のジャグ配列・ジャグ配列の多次元配列・多次元配列の多次元配列
多次元配列の要素の型をジャグ配列にすることにより、ジャグ配列の多次元配列を構築することができます。 また逆に、ジャグ配列の要素の型を多次元配列にすることにより、多次元配列のジャグ配列を構築することもできます。 さらに、多次元配列の多次元配列も構築することができます。
一般に、このようなデータ型が必要になる場合はまれで、必要になったとしてもよりシンプルなデータ型で表現可能である場合がほとんどだと思われます。 以下はあくまでこのようなことも可能ということを示す目的で、多次元配列のジャグ配列・ジャグ配列の多次元配列・多次元配列の多次元配列を構築する例をあげます。
配列・多次元配列・ジャグ配列を分類する
実行時にオブジェクトが配列か、多次元配列か、多段ジャグ配列かどうかを調べるたい場合、型情報(Type)を参照することで検証することができます。
型情報からは、まずType.IsArrayプロパティによって、型が配列(多次元配列・多段ジャグ配列を含む)かどうかを判別することができます。 また、Array.Rankプロパティを参照することにより、Rankが1
なら配列またはジャグ配列、2
以上なら多次元配列として分類することができます。 さらに、Type.GetElementTypeメソッドを呼び出すことにより、配列の要素の型を取得することができるため、1次元の配列なのかジャグ配列なのか、またジャグ配列の場合は何段なのかを知ることができます。
これを具体的なコードにすると、次のようになります。