JAVA　基本文法[5] - 配列

##### 「配列」とは
「配列」とは、「同じ型の複数の値をまとめて扱うためのデータ構造」。
※ある一定のルールに従ってデータを格納する形式を「データ構造」という。
変数のような個々の独立したデータではなく、１つのグループに属するデータをまとめて扱える特徴を持つ。

変数のような箱が連続して並んでおり、この箱１つ１つを「要素」という。
配列の各要素には同一種類のデータしか格納できない。つまり１つの要素に数値、１つ目の要素に文字列は出来ない。
配列内の各要素には、番号が付いている。この番号を「添え字（index）」と言い、「先頭から順に、0から始まる決まりになっている」。
<img width="340"
src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1162001/efa867f9-1b17-8a58-783d-dbe9f2b41849.png">

---
###### 配列の書き方
●配列の作成方法
<img width="300"
src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1162001/d818e842-f22f-ce57-b868-1504f0fb5d95.png">
① 「配列変数」の宣言（作成）
配列変数は、一般的な変数と捉えないこと。

::: note
配列変数の宣言（作成）
要素の型[] 配列変数名;
:::
②-1 「要素の作成」と②-2「（配列変数へ）代入」
「new演算子」を用いて、指定された型の要素を[]内に指定した数だけ作成。そして作成された要素は、代入演算子（=）で配列変数に代入される。

::: note
要素の作成と代入
配列変数名 = new 要素の型[要素の数]
:::
上記を１行にしたものは…↓

::: note
「配列変数の宣言」と「要素の作成と代入」を同時に行う文
要素の型[] 配列変数名 = new 要素の型[要素の数]
:::
```Main.java
//例)一行にまとめた文
int[] scores; //配列変数の宣言
scores = new int[5]　//要素の作成と代入

//例）二行にまとめた文
int[] scores = new int[5] //配列変数の宣言,要素の作成と代入
```


●配列の長さを調べる（配列の要素数はいくつか）

::: note
配列の要素数の取得
配列変数名.length
:::
※教科の平均点を求める処理に便利！！
```Main.java
//例）配列の要素数はいくつか調べる。
public class Main {
  public static void main(String[] args) {
    int[] scores = new int[5];
    int num = scores.length; //５になる
    System.out.println("要素の数: " + num);
  }
}
```

●配列の利用

::: note
「配列変数名[添え字]」でそれぞれの要素を読み書きできる。
:::
```Main.java
//例）　配列の要素に値を代入する
public class Main {
  public static void main(String[] args) {
    int[] scores;
    scores = new int[5];
    scores[1] = 30; //2つ目の要素scores[1]に30を代入する
    System.out.println(scores[1]); //2つ目の要素scores[1]の中身を出力
  }
}
```

●「配列の要素は自動的に初期化される」
※変数の場合…変数の値を取り出す前には、必ず値を代入して初期化しなければならない。初期化していない変数を利用するとコンパイルエラーになる。

「配列の場合は要素は自動的に初期化される」ので、コンパイルエラーにはならない。
要素がどのような値で初期化されるかは、要素の型によって決まっている。
|型|初期化の値|
|:-:|:-:|
|intやdoubleなどの数値の型|0|
|boolean型|false|
|String型|null|

```Main.java
//例)配列は自動的に初期化される
public class Main {
  public static void main(String[] args) {
    int[] scores = new int[5];
    System.out.println(scores[0]); //0が出力される為コンパイルエラーにはならない。
  }
}
/*実行結果
0
*/
```

●配列の作成と初期値の代入までをまとめて行う省略記法

::: note
「配列作成と初期化の省略記法」
・要素の型[] 配列変数名 = new 要素の型[] {値1, 値2, 値3, …};
・要素の型[] 配列変数名 = {値1, 値2, 値3, …};
:::
```Main.java
int[] scores1 = new int[] {20, 40, 60};
int[] scores2 = {20, 40, 60};
```
---
##### 配列と例外（配列時のエラー）
（下記は、範囲外要素の利用による例外の発生である）
```Main.java
//例)
public class Main {
  public static void main(String[] args) {
    int[] scores = {20, 30, 40, 50, 80};
    int sum = scores[1] + scores[2] + scores[3] + scores[4] + scores[5];
//   ↑上記の行は合計の算出
    int avg = sum / scores.length;　//平均の算出
    System.out.println("合計点:" + sum); //合計の表示
    System.out.println("平均点:" + avg); //平均の表示
  }
}
//実行結果（エラー文）
java.lang.ArrayIndexOutOfBoundsException: Index 5(Main.java:4)
```
エラーの原因は…
配列scoresの要素数は５つなので要素の添え字は[0]~[4]。
しかしこのコードの4行目で、scores[5]を使っているためエラーが発生している。
このように、存在しない要素をコード内で使用してもコンパイルは成功する。しかしプログラムを動かすと
その行を処理したときに、
「ArrayIndexOutOfBoundsException」というエラーが出てプログラムは中断される。
このようなエラーを特に「例外（Exception：エクセプション）」という。

「ArrayIndexOutOfBoundsException」が発生したら、「エラーの原因が存在しない要素を使おうとしたから」と捉えておこう！

ArrayIndexOutOfBoundsException
⇨Arrayは配列。Indexは添え字。OutOfBoundsは範囲外。という意味である。

---
##### 配列を使った文 [繰り返しと配列のよくある4パターン]
添え字には値（リテラル）だけでなく「変数を指定することも出来る」。

●ループによる全要素の利用
配列の最初から最後までの全要素を順にアクセスする。(=「配列を回す」という)
もし配列内の要素の数が変更してもfor文には影響はない。

::: note
for文で配列を回す。
for (int i = 0; i < 配列変数名.length; i++) {
 配列変数名[i]を使った処理
}
※ループ変数名は任意でOK
:::
```Main.java
//例）「for文を使って配列を扱う」
public class Main {
  public static void main(String[] args) {
    int[] scores = {20, 30, 40, 50, 80};
    for (int i = 0; i < scores.length; i++) {
      System.out.println(scores[i]);//ループの度にiの値が0~4で変化する。
    }
  }
}
/*実行結果
20
30
40
50
80
*/
```
添え字にはループ変数を指定している為、ループの度に、0→1→2→3→4と変化し、結果として配列内の先頭のscores{0}～[4]までを順にアクセスすることになる。

●ループによる集計
```Main.java
//例）点数管理のプログラム
public class Main {
  public static void main(String[] args) {
    int[] scores = {20, 30, 40, 50, 80};
    int sum = 0; //集計結果を入れる為の変数sumを0で初期化して準備
    for (int i = 0; i < scores.length; i++) {
      sum += scores[i]; //1科目ずつsumに合算
    }
    int avg = sum / scores.length;
    System.out.println("合計点:" + sum);
    System.out.println("平均点:" + avg);
  }
}
/*実行結果
合計点:220
平均点:44
*/
```

```Main.java
//例）条件と一致する要素の数を数えるカウント集計（下記は50点以上の科目の数を調べるプログラム)
public class Main {
  public static void main(String[] args) {
    int[] scores = {20, 30, 40, 50, 80};
    int count = 0;
    for (int i = 0; i < scores.length; i++) {
      if (scores[i] >= 50) {//ここの行から下3行は、条件に合致する要素があればカウントするという意味。
        count++;
      }
    }
    System.out.println("50点以上の科目の数は:" + count);
  }
}
/*実行結果
50点以上の科目の数は:2
*/
```

●添え字に対応した情報の利用
```Main.java
/*例　DNAの記号をランダムに表示する。
0～3の整数がランダムに格納された10個の要素を持つ配列seqがある。
また、画面には0123という数字ではなく、それぞれの整数に対応されたA,T,G,Cが表示されるように。
*/

public class Main {
  public static void main(String[] args) {
    int[] seq = new int[10];

    // 塩基配列をランダムに生成
    for (int i = 0; i < seq.length; i++) {
      seq[i] = new java.util.Random().nextInt(4);
    }

    // 生成した塩基配列の記号を表示
    for (int i = 0; i < seq.length; i++) {
      switch (seq[i]) {
        case 0:
          System.out.print("A ");
          break;
        case 1:
          System.out.print("T ");
          break;
        case 2:
          System.out.print("G ");
          break;
        case 3:
          System.out.print("C ");
          break;
      }
    }
  }
}
/*実行結果　実行する度に結果は変わる。
C T T T T T C A A A
*/
```

↑このよう冗長な文ではなく、更にシンプルにも書くことができる。
「// 生成した塩基配列の記号を表示」内のswitch文を以下に書き直すことができる。
```Main.java
char[] base = {'A', 'T', 'G', 'C'};
System.out.print(base[seq[i]] + " ");
```

●更にすっきり書ける「拡張for文」
拡張for文では、「ループ変数や配列の添え字を記述する必要がない」。

::: note
拡張for文で配列を回す
for (要素の型 任意の変数名 : 配列変数名) {…}
:::

```Main.java
//例　拡張for文
public class Main {
  public static void main(String[] args) {
    int[] scores = {20, 30, 40, 50, 80};
    for (int value : scores) {
      System.out.println(value);
    }
  }
}
```

---
##### 配列の舞台裏
（ここから）詳細を後ほど入力

```Main.java
//配列の裏側を理解するには…
public class Main {
  public static void main(String[] args) {
    int[] arrayA = {1, 2, 3};
    int[] arrayB;
    arrayB = arrayA;
    arrayB[0] = 100;
    System.out.println(arrayA[0]);
  }
}
/*実行結果
100
*/
```
●メモリと変数
●メモリと配列
（ここまで）詳細を後ほど入力

---
##### 配列の後片付け
配列変数も、寿命は自分が宣言されたブロックが終了するまで。
しかし、「配列の要素自体はブロックが終了しても寿命を迎えない」。
その為、事実上メモリ内のゴミとなる為、溜まり続けるとメモリを圧迫してしまう。（ゴミというのは、どの変数からも参照されなくなったメモリ領域）
本来ならメモリの後片付けをプログラムで指示する必要があるのだが…
Javaには「ガベージコレクション」という仕組みが常に動いており、自動的にゴミを探して片づけてくれる。

●null（何もないという意味）
意図的にガベージコレクションで、配列を参照させないようにする(参照を切る)には、参照型の変数に「null」を代入する。
※参照型：配列やオブジェクトの場所情報が入った変数のこと。

nullの特徴
・int[]型などの参照型変数に代入すると、その変数は何も参照しなくなる。
・int型などの基本型変数には代入できない。

```Main.java
array = null;
```

●例外「NullPointerException」
「NullPointerException」とは、
nullが格納されている配列変数を利用しようとすると発生する例外。

---
##### 多次元の配列
ここまで学んできた配列は、1次元配列という。
これに縦の並びを加えると、「2次元配列」になる。データを表のような形で扱いたいときに便利。

イメージ図
<img width="300"
src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1162001/aebe38e7-a3ce-d4e5-385d-98763c1ca77e.png">
また、2次元以上の配列を多次元配列と呼ぶ。

::: note
2次元配列の宣言
要素の型[][] 配列変数名 = new 要素の型[行数][行列]
:::

::: note
2次元配列の要素の利用
配列変数名[行の添え字][列の添え字]
:::

```Main.java
//２次元配列の利用
public class Main {
  public static void main(String[] args) {
    int[][] scores = new int[2][3];
    scores[0][0] = 40;
    scores[0][1] = 50;
    scores[0][2] = 60;
    scores[1][0] = 80;
    scores[1][1] = 60;
    scores[1][2] = 70;
    System.out.println(scores[1][1]);
  }
}
```

```Main.java
//親配列と子配列の要素数を表示(2次元配列)
public class Main {
  public static void main(String[] args) {
    int[][] scores = {{40, 50, 60}, {80, 60, 70}}; //このような初期化が必要
    System.out.println(scores.length); //2が出力される
    System.out.println(scores[0].length);　//3が出力される
  }
}
```