Unity学習記録 #009 コレクション（配列）

# コレクション
複数のデータをひとまとめにして管理できる変数で代表的なものは３つ
・配列（要素数が固定されて扱われる代表的なデータ構造）
・リスト（要素の追加、削除、挿入ができるデータ構造）
・ディクショナリ（要素に "キー" と "値" がセットで格納させるデータ構造）

今回は配列（Array）について学習していきます。

## 配列
配列は同じ型を持つ値を一つの変数内に連続して格納できます。
配列の宣言時に要素数が決まり、以降は要素の数を変更することはできません。各要素の値の更新はできます。
要素を呼び出すときはインデックスと呼ばれる０から始まる連番で要素番号を指定して呼び出します。

### 配列の宣言について
配列の構文は次のようになります。
```csharp
型名[] 配列名 = new 型名[] {要素1, 要素2, 要素3, ... };
```
実際にコードを書を書いてみます。部分的に省略した書き方もでき、次のようになります。
```csharp
//整数
int[] a = {1,2,3,4,5};
int[] b = new int[]{1,2,3,4,5};
int[] c = new int[5]{1,2,3,4,5};
```
どの記述方法も整数型の配列に初期値を設定している書き方で、どれも同じ扱いができます。３つ目の書き方は、一目で要素数が分かるので、コードの可読性が高くなります。プロジェクト内では混在させずに、どれか一つの書き方で統一した方がよさそうです。

他の型での宣言方法も同じです。
```csharp
//文字列
string[] a = {"斎藤", "鈴木", "高橋", "田中", "伊藤"};
string[] b = new string[]{"斎藤", "鈴木", "高橋", "田中", "伊藤"};
string[] c = new string[5]{"斎藤", "鈴木", "高橋", "田中", "伊藤"};

//浮動小数
float[] a = { 1.0f, 3.4f, 5.2f, -3.5f, 8.2f };
float[] b = new float[] { 1.0f, 3.4f, 5.2f, -3.5f, 8.2f };
float[] c = new float[5] { 1.0f, 3.4f, 5.2f, -3.5f, 8.2f };
```
配列は要素の数を固定して使用する変数なので、宣言時に要素の数を指定する必要があります。
これまでは配列の宣言時に初期値も設定する記述でしたが、例えば初期値を設定しない場合に、次のような記述ができません。
```csharp
int[] a;
int[] b = new int[];
```
上記２つの書き方は要素数が特定できないのでエラーとなります。
配列に初期値を設定しない時の変数の宣言は次のように記述します。
```csharp
//初期値を設定しない配列の型の宣言
int[] b = new int[5];
```
初期値を設定しない場合は、要素数を指定するために大括弧に要素数を記述します。
この場合、初期値を設定しなくても要素数が "5" と指定されているのでOKです。


### 要素の呼び出し
配列の要素はインデックス番号を指定して呼び出します。配列の要素は０から順にインデックス番号が割り当てられています。
```csharp
//要素の呼び出し
string[] names = new string[]{"斎藤", "鈴木", "高橋", "田中", "伊藤"};

Debug.Log(names[0]);
Debug.Log(names[3]);
```
インデックス番号は整数値なので、変数で指定することもできます。
```csharp
//インデックス番号に変数を使用
string[] names = new string[]{"斎藤", "鈴木", "高橋", "田中", "伊藤"};
int number = 3;

Debug.Log(names[number]);
```
配列の要素数は簡単に取り出すことができます。
```csharp
//配列の要素数を取得
string[] names = new string[] { "斎藤", "鈴木", "高橋", "田中", "伊藤" };
int num = names.Length;

Debug.Log(num);
```
for文と組み合わせて、配列の全ての要素に処理を加えるコードの書き方はよく使われます。
```csharp
string[] names = new string[] { "斎藤", "鈴木", "高橋", "田中", "伊藤" };

for (int i = 0; i < names.Length; i++)
{
    Debug.Log($"{names[i]}さん、こにちは。");
}
```

### 一次元配列
これまで記述した、上記のサンプルコードはどれも一次元配列です。１行に要素が連続で格納されているイメージです。
ここで既定値についても少し触れておきます。型の宣言時に初期値を設定しない場合の要素の値についてです。
```csharp
int[] c = new int[3];
c[1] = 6;

for (int i = 0; i < c.Length; i++)
{
    Debug.Log(c[i]);
}
```
整数型で要素数が３の配列変数を宣言して、一つの要素には値を代入しました。このコードを実行すると、初期値を設定しなかった要素の出力値は０になっていることが分かります。
配列の要素には既定値があり、数値型で初期値が設定されない場合の既定値は０です。
他の型にも既定値があり、それぞれ次のように設定されています。

・数値型（int,float）の既定値：0
・論理型（bool）の既定値：false
・参照型（string）の既定値：null


### 二次元配列
２次元配列は表形式でデータを管理します。スプレッドシートのセルが要素として扱われるイメージです。宣言の書き方が２つありましたので紹介します。
まず１つ目の宣言方法です。
```csharp
//宣言時に初期値を設定
int[,] a = new int[,] { {1, 2, 3 },{4, 5, 6 },{7, 8, 9 } };

//配列の宣言だけ行い、後から値を代入
int[,] b = new int[3,3];

int n = 0;
for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 3; j++)
    {
        b[i, j] = n;
        Debug.Log(b[i, j]);
        n++;
    }
}
```
こちらは比較的新しい２次元配列の宣言の書き方のようです。データが行と列で格納された構造になっています。
C#では使用できますが、他のプログラム言語と比較したときにコードの書き方の互換性がありません。

２つ目の書き方を見てみます。
```csharp
//１次元配列の変数
int[] a = new int[] { 1, 2, 3 };
int[] b = new int[] { 4, 5 };
int[] c = new int[] { 6, 7, 8, 9, 10 };

//ジャグ配列の宣言
int[][] array = new int[3][];
array[0] = a;
array[1] = b;
array[2] = c;

Debug.Log(array[2][3]);
```
２つ目は配列の中に配列を格納した構造になっています。ジャグ配列とも呼ばれます。
ジャグ配列では、中に入っている配列がそれぞれ異なる要素数でも問題ありません。

少しだけ、２次元配列を使った簡単な表計算をしてみます。５人のプレーヤーが３回の競技を行った時の点数を記録したデータがあるとします。このデータから個人の平均スコアを計算してみます。
```csharp
string[] names = new string[] { "斎藤", "鈴木", "高橋", "田中", "伊藤" };
int[] score1 = new int[] {79, 64, 68, 85, 53 };
int[] score2 = new int[] {63, 48, 84, 55, 74 };
int[] score3 = new int[] {76, 85, 59, 44, 62 };

//一次元配列のデータを２次元配列に代入して一つの変数にまとめる
int[][] scores = new int[][] {score1, score2, score3 };

//汎用的に使えるように、行数と列数を調べる
int row = scores.GetLength(0);  //行の要素数を調べる
int col = scores[0].GetLength(0);  //列の要素数を調べる

//プレーヤー個人のスコア平均の計算
for (int i = 0; i < col; i++)
{
    float sum = 0.0f;  //合計値を計算するために初期化
    for (int j = 0; j < row; j++)
    {
        sum += scores[j][i];
    }
    float scoreAverage = sum / row;  //スコアの平均計算

    Debug.Log($"{names[i]}さんのスコア平均{scoreAverage}");
}
```
スプレッドシートで計算したときの結果を比較用として記載します。
![無題のスプレッドシート.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822122/ce7c9a94-7d11-8ccd-b71c-f5b60ba30867.jpeg)

まずは簡単なコードを見ながら配列について勉強してみました。

## 追記情報
### 2024/07/08（配列長さの取得について）
配列の要素数の取得について、説明ではさらっと記述していましたので、整理して記述します。
説明では配列に設定した要素の数をそのまま「要素数」と記述していますが、「配列長さ」と表現する方が一般的な表現のようです。

配列長さを取得するメソッド
・「.Length」：配列要素全体の長さを取得
・「.GetLength( )」：次元を指定して長さを取得

コードを書いてみます。
```csharp
//配列長さだけを指定した配列の宣言
int[] a = new int[3];
int[,] b = new int[4, 5];
int[,,] c = new int[3, 2, 4];

//1次元配列の配列長さ取得
Debug.Log(a.Length);

//2次元配列の配列長さ取得
Debug.Log(b.Length); //全体の配列長さ
Debug.Log(b.GetLength(0)); //1次元目の配列長さ
Debug.Log(b.GetLength(1)); //2次元目の配列長さ

//3次元配列の配列長さ取得
Debug.Log(c.Length); //全体の要素数
Debug.Log(c.GetLength(0)); //1次元目の配列長さ
Debug.Log(c.GetLength(2)); //3次元目の配列長さ
```
１次元配列では次元が１つなので全体の長さを取得する「.Length」メソッドが使われます。多次元配列でそれぞれの次元の長さを調べる場合は「.GetLength( )」で次元を指定して長さを調べます。