●多次元配列とは?
C#を学んでいくうえで、配列は欠かせない存在ですよね。
1次元配列はもう使いこなせているけど、多次元配列となると少し苦手意識があるという方も多いのではないでしょうか。
でも、安心してください。
この記事を読めば、多次元配列もスラスラ使えるようになります。
多次元配列というのは、複数の次元を持つ配列のことです。
例えば、1次元配列が横一列に並んだデータだとしたら、2次元配列は縦と横の2つの次元を持つ、マス目状のデータといったイメージです。
さらに、3次元、4次元と次元を増やしていくこともできます。
○多次元配列の特徴と利点
多次元配列の大きな特徴は、複雑な構造のデータを表現できるということです。
例えば、学生の成績管理をするときに、1次元配列だと各教科の点数を並べるだけになってしまいます。
でも、2次元配列を使えば、縦に生徒の名前、横に教科名を並べて、それぞれの交点に点数を入れることができます。
こうすることで、データの構造が明確になり、扱いやすくなるんです。
また、多次元配列を使うことで、コードがシンプルになるというメリットもあります。
例えば、3つの値を持つデータを100個扱いたい場合、1次元配列だと3つの配列を用意する必要がありますが、2次元配列なら1つの配列で済みます。
こうすることで、コードの見通しが良くなり、バグも発生しにくくなるんですね。
○1次元配列との違い
では、具体的に1次元配列と多次元配列の違いを見ていきましょう。
下記のコードをご覧ください。
1次元配列は []
の中に要素数を指定するだけですが、2次元配列は [,]
とカンマで区切って、縦と横の要素数を指定します。
また、要素へのアクセス方法も異なります。
1次元配列は []
の中にインデックスを指定しますが、2次元配列は [縦のインデックス, 横のインデックス]
という形で指定します。
このように、多次元配列は1次元配列とは異なる特徴を持っています。
●多次元配列の宣言方法
さて、多次元配列についてイメージが湧いてきたところで、早速宣言方法について見ていきましょう。
先ほど、2次元配列の宣言方法は少し触れましたが、ここではもう少し詳しく解説していきます。
多次元配列の宣言は、1次元配列の宣言とほぼ同じです。違いは、[]
内のカンマの数だけですね。
カンマの数が、配列の次元数-1になります。
つまり、2次元配列なら [,]
、3次元配列なら [,,]
というように表現します。
○サンプルコード1:2次元配列の宣言
まずは、2次元配列の宣言方法をサンプルコードで見てみましょう。
このコードは、3行4列の2次元配列 array2d
を宣言しています。
[3, 4]
の部分で、配列のサイズを指定しているんですね。
実行結果を見てみましょう。
GetLength()
メソッドを使うことで、各次元の長さを取得できます。
引数の 0
は1次元目(行)、1
は2次元目(列)を表しています。
○サンプルコード2:3次元配列の宣言
次に、3次元配列の宣言方法も見ておきましょう。
このコードは、2×3×4の3次元配列 array3d
を宣言しています。
[2, 3, 4]
の部分で、各次元のサイズを指定しています。
実行結果を確認してみましょう。
やはり GetLength()
メソッドで、各次元の長さを取得できますね。
引数の 0
、1
、2
は、それぞれ1次元目、2次元目、3次元目を表しています。
○サンプルコード3:jagged配列の宣言
最後に、jagged配列(ギザギザ配列)の宣言方法も触れておきましょう。
jagged配列は、「配列の配列」とも呼ばれ、各行の要素数が異なる配列を表現できます。
このコードでは、まず int[][]
で、配列の配列を宣言しています。
次に、各行ごとに new
で配列を作成し、サイズを指定しています。
これにより、各行のサイズが異なるjagged配列が作成できました。
実行結果を見てみましょう。
Length
プロパティを使うことで、各次元の長さを取得できます。
jaggedArray.Length
は配列の行数を、jaggedArray[i].Length
は各行の要素数を表しています。
●多次元配列の要素へのアクセス方法
配列を宣言したら、次は要素にアクセスしてデータを読み書きしたいですよね。
1次元配列では array[i]
のように、インデックスを1つ指定するだけでしたが、多次元配列ではどうなるでしょうか。
実は、多次元配列でも基本的な考え方は同じです。
ただ、インデックスをカンマ区切りで複数指定する必要があります。
例えば、2次元配列なら array[i, j]
、3次元配列なら array[i, j, k]
といった具合です。
○サンプルコード4:2次元配列の要素にアクセスする
まずは、2次元配列の要素にアクセスする方法から見ていきましょう。
先ほど宣言した array2d
を使って、要素の読み書きをしてみます。
array2d[0, 0] = 1;
のように、[行のインデックス, 列のインデックス]
を指定して要素に値を設定できます。
また、Console.WriteLine(array2d[0, 0]);
のように、同じ記法で要素の値を取得することもできます。
ちょっとややこしいですが、行と列のインデックスを逆に指定しないように注意しましょう。
array2d[i, j]
は、「i行目のj列目の要素」を表しています。
○サンプルコード5:3次元配列の要素にアクセスする
次に、3次元配列の要素へのアクセス方法も確認しておきましょう。
先ほどの array3d
を使って、要素の読み書きをしてみます。
3次元配列の場合は、[1次元目のインデックス, 2次元目のインデックス, 3次元目のインデックス]
のように、3つのインデックスを指定します。
これにより、立体的な配列の要素にアクセスできるわけです。
○サンプルコード6:jagged配列の要素にアクセスする
最後に、jagged配列の要素にアクセスする方法も見ておきましょう。
jagged配列は「配列の配列」なので、アクセス方法も少し特殊です。
jagged配列の要素にアクセスするには、jaggedArray[i][j]
のように、2つの []
を使います。
最初の [i]
で「i番目の配列」を指定し、次の [j]
で「その配列のj番目の要素」を指定する、というイメージですね。
●多次元配列の要素数の取得方法
多次元配列を使いこなすには、要素へのアクセス方法だけでなく、配列の大きさを知ることも重要ですよね。
配列の要素数を把握しておくことで、ループ処理などを正しく記述できます。
では、多次元配列の要素数を取得するにはどうすればいいのでしょうか。
実は、Length
プロパティと GetLength()
メソッドを使うことで、簡単に要素数を知ることができるんです。
○サンプルコード7:配列の次元数と各次元の長さを取得する
早速、サンプルコードを見ながら、多次元配列の要素数を取得する方法を確認していきましょう。
まず、Rank
プロパティを使うことで、配列の次元数を取得できます。
2次元配列 array2d
は Rank
が2、3次元配列 array3d
は Rank
が3になっていますね。
一方、jagged配列 jaggedArray
は、「配列の配列」なので、Rank
は1になります。
次に、GetLength()
メソッドを使って、各次元の長さを取得しています。
GetLength(0)
は1次元目の長さ、GetLength(1)
は2次元目の長さ、というように、引数で次元を指定します。
jagged配列の場合は、Length
プロパティで各サブ配列の長さを取得できます。
このように、Rank
プロパティと GetLength()
メソッドを使えば、多次元配列の次元数と各次元の長さを簡単に知ることができます。
○GetLength()メソッドとGetLongLength()メソッドの違い
ところで、GetLength()
メソッドには、GetLongLength()
メソッドというよく似た名前のメソッドがあるのをご存知でしょうか。
一体どう違うのでしょうか。
実は、GetLength()
メソッドが返すのは int
型の値で、GetLongLength()
メソッドが返すのは long
型の値なんです。
つまり、GetLength()
では取得できる要素数の上限が約21億個なのに対し、GetLongLength()
では約922京個まで取得できるわけです。
とはいえ、多くの場合は GetLength()
で十分だと思います。
要素数が21億を超えるような巨大な配列を扱うことはあまりないでしょうから。
でも、GetLongLength()
の存在は知っておいて損はないですよね。
●多次元配列への要素の追加方法
さて、多次元配列の要素数を取得する方法がわかったところで、今度は配列に新しい要素を追加する方法について見ていきましょう。
1次元配列では、Array.Resize()
メソッドを使って配列のサイズを変更し、新しい要素を追加することができました。しかし、多次元配列の場合は少し事情が異なります。
実は、C#の多次元配列は、一度作成するとサイズを変更することができないんです。
ええっ、じゃあ要素を追加することはできないの?と思われるかもしれません。
でも、ご安心ください。サイズを変更できないからと言って、要素を追加する方法がないわけではありません。
ちょっとした工夫をすれば、多次元配列に要素を追加することは可能なんです。
○サンプルコード8:Array.Resize()メソッドで配列のサイズを変更する
まずは、Array.Resize()
メソッドを使って、多次元配列のサイズを変更してみましょう。
このコードを実行すると、コンパイルエラーが発生します。
Array.Resize()
メソッドは、1次元配列のサイズを変更することはできますが、多次元配列には使えないんですね。
じゃあ、どうすればいいのでしょうか。
実は、新しいサイズの配列を作成し、元の配列の要素をコピーする、という方法があります。
○サンプルコード9:配列をコピーして新しい配列を作成する
それでは、配列をコピーして新しい配列を作成し、要素を追加する方法を見ていきましょう。
実行結果
このコードでは、まず新しいサイズ(4行5列)の配列 newArray2d
を作成しています。
そして、for
ループを使って、元の配列 array2d
の要素を newArray2d
にコピーしています。
最後に、newArray2d[3, 4] = 100;
として新しい要素を追加しています。
実行結果を見ると、新しい配列 newArray2d
には、元の配列の要素がコピーされ、さらに新しい要素が追加されていることがわかります。
●多次元配列での異なる型の格納可能性
ここまで、多次元配列の基本的な使い方について見てきましたが、ふと疑問に思ったことはありませんか?
それは、「多次元配列には異なる型の要素を格納できるのだろうか」ということだと勝手に推測します。
C#の配列は、通常、同じ型の要素しか格納できません。
例えば、int
型の配列には int
型の値しか格納できませんし、string
型の配列には string
型の値しか格納できません。
しかし、多次元配列の場合はどうでしょうか。
○Objectを使った異なる型の格納
実は、多次元配列でも異なる型の要素を格納する方法があります。
それは、Object
型の配列を使うことです。
Object
型は、C#のすべての型の基本となる型で、任意の型の値を格納することができます。
実行結果
このコードでは、Object
型の2次元配列 array
を作成し、int
、string
、bool
、double
、int[]
、Person
オブジェクトなど、様々な型の要素を格納しています。
そして、for
ループを使って配列の要素を順番に表示しています。
実行結果を見ると、異なる型の要素が配列に格納され、正しく表示されていることがわかります。
Object
型の配列を使えば、このように多次元配列でも異なる型の要素を扱うことができるんです。
○型安全性の問題と対処法
ただし、Object
型の配列を使うことには、型安全性の問題があることに注意が必要です。
型安全性とは、コンパイル時に型のチェックが行われ、型に関するエラーを防ぐことができる性質のことです。
Object
型の配列では、要素を取り出すときに明示的なキャストが必要になります。
例えば、int
型の要素を取り出すには、(int)array[0, 0]
のようにキャストしなければなりません。
このキャストが適切でない場合、実行時に InvalidCastException
が発生する可能性があります。
この問題を避けるためには、型安全性の高い方法として、ジェネリックの配列を使うことができます。
ジェネリックを使えば、配列の要素の型を指定することができ、型のチェックがコンパイル時に行われるようになります。
このコードでは、MyClass<T1, T2>
という2つの型パラメータを持つジェネリッククラスを定義し、その配列を作成しています。
これにより、型安全性が保たれ、コンパイル時のチェックが行われるようになります。
●2次元配列の動的な宣言方法
これまで、多次元配列の基本的な使い方について学んできました。
宣言方法、要素へのアクセス方法、要素数の取得方法、要素の追加方法、そして異なる型の要素の格納方法まで、一通り理解が深まったのではないでしょうか。
さて、ここからは少し応用的な内容に踏み込んでいきたいと思います。
それは、2次元配列を動的に宣言する方法です。
通常、配列は宣言時にサイズを指定しますが、状況によってはサイズが予め決まっていないこともあります。
そんなときに役立つのが、動的な配列の宣言です。
C#では、配列の宣言と同時に要素を初期化する方法と、Array.CreateInstance()
メソッドを使う方法の2つがあります。
○サンプルコード10:配列の宣言と同時に要素を初期化する
まずは、配列の宣言と同時に要素を初期化する方法から見ていきましょう。
この方法を使えば、配列のサイズを明示的に指定せずに、要素の数に応じた配列を作成できます。
実行結果
このコードでは、new int[][] { ... }
という構文を使って、配列の宣言と同時に要素を初期化しています。
各行の要素数が異なっていても、問題なく配列を作成できるのがポイントです。
for
ループを使って配列の要素を表示していますが、各行の要素数に合わせてループの回数が自動的に調整されています。
これにより、動的にサイズが決まる配列を簡単に扱うことができます。
○サンプルコード11:Array.CreateInstance()メソッドで配列を動的に作成する
もう一つの方法は、Array.CreateInstance()
メソッドを使って配列を動的に作成する方法です。
このメソッドを使えば、配列のサイズを変数で指定することができます。
実行結果
このコードでは、Array.CreateInstance()
メソッドを使って、rows
行 cols
列の2次元配列を作成しています。
typeof(int)
は配列の要素の型を指定しています。
作成した配列に値を設定するには、SetValue()
メソッドを使います。
第1引数が設定する値、第2引数以降が各次元のインデックスです。
逆に、値を取得するには GetValue()
メソッドを使います。
●2次元配列から特定の行を取り出す方法
2次元配列を使っていると、特定の行のデータだけを取り出したいという場面に遭遇することがあります。
例えば、成績データの中から特定の生徒の成績を抽出したい場合などですね。
そんなときに役立つのが、LINQの Select()
メソッドや、for
文を使った方法です。
これらを使えば、2次元配列から特定の行を簡単に取り出すことができます。
○サンプルコード12:LINQのSelect()メソッドで行を取り出す
まずは、LINQの Select()
メソッドを使って、2次元配列から特定の行を取り出す方法を見ていきましょう。
Select()
メソッドは、配列やリストから特定の条件に合う要素を選択するために使われます。
実行結果
このコードでは、まず array2d.Cast<int>()
で2次元配列を1次元配列に変換しています。
これにより、Select()
メソッドが使えるようになります。
そして、Skip()
メソッドで最初の行をスキップし、Take()
メソッドで2行目の要素を取得しています。
最後に ToArray()
で配列に変換し、string.Join()
で要素を連結して表示しています。
このように、LINQの Select()
メソッドを使えば、2次元配列から特定の行を簡単に取り出すことができます。
○サンプルコード13:for文を使って行を取り出す
LINQを使わずに、for
文を使って2次元配列から特定の行を取り出すこともできます。
for
文を使う方法は、より基本的で理解しやすいかもしれません。
実行結果
このコードでは、まず取り出したい行のインデックス rowIndex
を指定しています。
そして、selectedRow
という1次元配列を作成し、for
文を使って array2d
の rowIndex
行目の要素を順番に代入しています。
最後に、string.Join()
を使って selectedRow
の要素を連結して表示しています。
このように、for
文を使っても、2次元配列から特定の行を取り出すことができます。
配列のインデックスを直接指定できるので、わかりやすいですね。
●多次元配列の初期化方法
多次元配列を使いこなすためには、要素の初期化方法を理解しておくことが大切です。
配列を宣言するだけでは、要素には何も値が設定されていません。適切な初期値を設定することで、配列をスムーズに活用できるようになります。
C#では、配列の宣言と同時に初期化する方法と、ループを使って初期化する方法の2つがよく使われます。
これらの方法を使いこなせば、多次元配列の初期化も難しくありません。
○サンプルコード14:配列の宣言と同時に初期化する
まずは、配列の宣言と同時に初期化する方法から見ていきましょう。
この方法を使えば、配列の要素に初期値を簡単に設定できます。
実行結果
このコードでは、new int[,] { ... }
という構文を使って、配列の宣言と同時に要素を初期化しています。
各行の要素を { }
で囲み、行と行の間はカンマで区切ります。
こうすることで、配列の要素に初期値を設定できます。
この例では、1から9までの数字を3×3の配列に格納しています。
for
ループを使って配列の要素を表示すると、設定した初期値が正しく格納されていることがわかります。
○サンプルコード15:ループを使って配列を初期化する
配列の要素を規則的な値で初期化したい場合は、ループを使うと便利です。
ループを使えば、配列のサイズに応じた初期化を自動的に行うことができます。
実行結果
このコードでは、まず3×4の2次元配列 array2d
を宣言しています。
そして、for
ループを使って配列の要素を初期化しています。
ここでは、array2d[i, j] = i * 10 + j;
という式で、各要素に値を設定しています。
この式では、行のインデックス i
に10を掛け、列のインデックス j
を足しています。
これにより、0, 1, 2, 3, 10, 11, … というような規則的な値で配列を初期化できます。
最後に、for
ループを使って配列の要素を表示し、初期化された値を確認しています。
●よくあるエラーと対処法
さて、ここまで多次元配列の様々な使い方について学んできましたが、実際にコードを書いていると、時々エラーに遭遇することがあります。
特に、配列を扱う際によく発生するエラーとして、IndexOutOfRangeException
、RankException
、ArrayTypeMismatchException
の3つが挙げられます。
これらのエラーは、配列の使い方を誤ったときに発生するのですが、初心者の方にとっては原因の特定や対処方法がわかりにくいかもしれません。
でも、ご安心ください。
ここでは、これらのエラーが発生する状況と、その対処方法について詳しく解説していきます。
○IndexOutOfRangeException(配列の範囲外にアクセスしようとした)
IndexOutOfRangeException
は、配列の範囲外の要素にアクセスしようとしたときに発生するエラーです。
例えば、次のようなコードがあるとします。
このコードでは、長さ3の配列 array
を宣言し、要素に値を設定しています。
しかし、最後の行で array[3]
という、配列の範囲外の要素にアクセスしようとしているため、IndexOutOfRangeException
が発生します。
このエラーを避けるためには、配列のインデックスが有効な範囲内であることを確認する必要があります。
つまり、インデックスは0から 配列の長さ - 1
までの範囲でなければなりません。
このように、for
ループを使って配列の要素にアクセスすれば、インデックスが有効な範囲内に収まるため、IndexOutOfRangeException
を避けることができます。
○RankException(配列の次元数が一致しない)
RankException
は、配列の次元数が一致しないときに発生するエラーです。
例えば、次のようなコードがあるとします。
このコードでは、2次元配列 array2d
を宣言した後、それを1次元配列 array1d
に代入しようとしています。
しかし、配列の次元数が一致しないため、RankException
が発生します。
このエラーを避けるためには、配列の次元数を一致させる必要があります。
つまり、配列の代入や引数の受け渡しの際には、次元数が同じ配列を使わなければなりません。
このように、次元数が一致する配列同士で代入や受け渡しを行えば、RankException
を避けることができます。
○ArrayTypeMismatchException(配列の型が一致しない)
ArrayTypeMismatchException
は、配列の型が一致しないときに発生するエラーです。
例えば、次のようなコードがあるとします。
このコードでは、int
型の配列 intArray
を宣言した後、それを object
型の配列 objectArray
に代入しようとしています。
しかし、配列の型が一致しないため、ArrayTypeMismatchException
が発生します。
このエラーを避けるためには、配列の型を一致させる必要があります。
つまり、配列の代入や引数の受け渡しの際には、同じ型の配列を使わなければなりません。
このように、型が一致する配列同士で代入や受け渡しを行えば、ArrayTypeMismatchException
を避けることができます。
まとめ
C#の多次元配列について、基本的な使い方から応用的な技術まで、幅広く解説してきました。
サンプルコードを交えながら、一つ一つ丁寧に解説してきたので、きっと多次元配列の扱い方がよくわかったのではないでしょうか。
また、よくあるエラーとその対処法も学んだので、実際にコードを書く際に役立つはずです。
これからは、ここで学んだ知識を活かして、多次元配列を使ったより効率的なプログラムを書いていってください。
最初は戸惑うこともあるかもしれませんが、練習を重ねるうちに、きっと自由自在に多次元配列を扱えるようになるでしょう。
本記事が、みなさんのC#プログラミングのスキルアップに少しでも貢献できたなら嬉しい限りです。
最後まで読んでいただき、本当にありがとうございました。