Java : 配列 (Array) の使い方
Javaの配列の使い方まとめです。
配列の宣言や初期化、要素へのアクセス方法などをご紹介します。
概要
配列とは、複数の変数(値)をまとめて管理するためのデータ構造です。
例えば、果物を表す3つの文字列
- リンゴ
- メロン
- バナナ
を管理したいとします。
まずは配列を使わずに、変数で管理してみましょう。
apple, melon, banana 変数にそれぞれの果物の文字列をいれます。
そして、それらをコンソールへ出力します。
final var apple = "リンゴ";
final var melon = "メロン";
final var banana = "バナナ";
System.out.println(apple);
System.out.println(melon);
System.out.println(banana);
// 結果
// ↓
//リンゴ
//メロン
//バナナ
これを配列に変えてみましょう。
final String[] fruits = {"リンゴ", "メロン", "バナナ"};
for (final var fruit : fruits) {
System.out.println(fruit);
}
// 結果
// ↓
//リンゴ
//メロン
//バナナ
変数で1つ1つ管理するよりかは、配列を使ったほうが管理しやすく感じますでしょうか?
今回は変数が3つなので、そこまで違いを感じないかもしれません。
しかし、これが100個くらい多くなってくるとどうでしょうか。
final var apple = "リンゴ";
final var melon = "メロン";
final var banana = "バナナ";
...
...
100個分続く
...
System.out.println(apple);
System.out.println(melon);
System.out.println(banana);
...
...
100個分続く
...
記述が大変なだけではなく、apple や melon といった変数名を考えるのも大変ですね。
また、これだけの量を手作業で書いていくと、うっかり1つ書き忘れてしまう、ということも十分起こりえます。
変数が増えると、その変数を管理するだけでも大変です。
結果として不具合の発生しやすい(メンテナンスしにくい)プログラムになります。
final String[] fruits = {"リンゴ", "メロン", "バナナ", ... 100個分続く ...};
for (final var fruit : fruits) {
System.out.println(fruit);
}
配列で管理すれば、fruits という変数が1つで済みます。
また、拡張for文 を使えば確実にすべての内容を出力できます。
補足
- 配列と似たデータ構造として リスト があります。
基本的には、配列よりリストを使うことをおすすめします。 - プリミティブ型(int, double, byteなど)を使いたい、限界までパフォーマンスを重視したい、というときには配列がよいかもしれません。
宣言
配列を宣言するには、変数宣言の型のところに [] を追加します。
int型を例に見てみましょう。
// 変数
int value;
// 配列
int[] array;
value は通常の変数です。
array は配列です。
もう少し詳しくいうと、1次元の配列です。
2次元の配列を宣言するには [] を追加してあげます。
3次元ではさらに追加します。
// 2次元配列
int[][] array2;
// 3次元配列
int[][][] array3;
これで配列としての変数を宣言できました。
ただし、これだけではまだ配列は使えません。
配列の変数には 初期化 が必要です。
初期化
初期化子
配列の初期値がわかっている場合は、初期化子(Array Initializers)を使うのが便利です。
{ } で初期値を指定してあげます。
※配列の内容を文字列に変換する便利なクラスとして Arrays を使っています。
// 1次元配列の例です。
final String[] array = {"a1", "a2", "a3", "a4"};
// [a1, a2, a3, a4]
System.out.println(Arrays.toString(array));
// 2次元配列の例です。
final String[][] array = {
{"a1-1", "a1-2", "a1-3"},
{"a2-1", "a2-2", "a2-3"}
};
// [[a1-1, a1-2, a1-3], [a2-1, a2-2, a2-3]]
System.out.println(Arrays.deepToString(array));
動的生成
new で生成してから初期化する方法です。
配列のサイズが、プログラム実行時まで決まらないときに使います。
※配列の length フィールドで、配列のサイズを取得できます。
final var size = 5;
final var array = new int[size];
for (int i = 0; i < array.length; i++) {
array[i] = i * 10;
}
// [0, 10, 20, 30, 40]
System.out.println(Arrays.toString(array));
final var size = 3;
final var array = new int[size][size];
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
array[i][j] = (i * 10) + j;
}
}
// [[0, 1, 2], [10, 11, 12], [20, 21, 22]]
System.out.println(Arrays.deepToString(array));
要素へのアクセス
インデックス
[ ] にインデックスを指定してアクセスします。
インデックスの範囲は
- 0 ~ 配列のサイズ -1
です。
もし、範囲外のインデックスを指定した場合は、ArrayIndexOutOfBoundsException が発生します。
final int[] array = {0, 10, 20, 30, 40};
System.out.println(Arrays.toString(array)); // [0, 10, 20, 30, 40]
System.out.println(array.length); // 5
//System.out.println(array[-1]); // ArrayIndexOutOfBoundsException
System.out.println(array[0]); // 0
System.out.println(array[1]); // 10
System.out.println(array[2]); // 20
System.out.println(array[3]); // 30
System.out.println(array[4]); // 40
//System.out.println(array[5]); // ArrayIndexOutOfBoundsException
array[2] *= 10;
array[3] *= 100;
System.out.println(Arrays.toString(array)); // [0, 10, 200, 3000, 40]
拡張for文
全要素にアクセスしたい場合は 拡張for文 が便利です。
final int[] array = {10, 20, 30, 40, 50};
for (int v : array) {
System.out.println(v);
}
// 結果
// ↓
//10
//20
//30
//40
//50
配列はObject
Javaプログラム言語では、配列は Object でもあります。
よって、Objectクラスの各メソッドを呼び出すことができます。
final int[] array = {0, 10, 20, 30, 40};
System.out.println(Arrays.toString(array)); // [0, 10, 20, 30, 40]
final var cls = array.getClass();
System.out.println(cls.getSimpleName()); // int[]
System.out.println(cls.isArray()); // true
System.out.println(cls.getSuperclass().getSimpleName()); // Object
final var cloned = array.clone();
System.out.println(Arrays.toString(cloned)); // [0, 10, 20, 30, 40]
equalsとhashCodeは注意
配列の equals と hashCode は、配列の内容を考慮しません。
final int[] array1 = {1, 2, 3};
final int[] array2 = {1, 2, 3};
// 内容は同じですが、ハッシュ・コードは違います。
System.out.println(array1.hashCode()); // 1426882610
System.out.println(array2.hashCode()); // 70686931
// equalsも"等しくない"を返します。
System.out.println(array1.equals(array2)); // false
もし配列の内容を考慮したい場合は、Arraysクラス を使います。
final int[] array1 = {1, 2, 3};
final int[] array2 = {1, 2, 3};
System.out.println(Arrays.hashCode(array1)); // 30817
System.out.println(Arrays.hashCode(array2)); // 30817
System.out.println(Arrays.equals(array1, array2)); // true
変更不可能(イミュータブル)な配列は作れない
不変性 を意識してプログラミングすることは、堅牢なプログラムを作る上で重要です。
残念ながら配列は変更不可能なインスタンスを生成できません。
変更不可能な配列を使いたい場合は、代わりに List を使うことをおすすめします。
- 配列を変更不可能なリストへ変換する例
final String[] array = {"a", "b", "c"};
System.out.println(Arrays.toString(array)); // [a, b, c]
// 変更可能!
array[1] = "ZZZ";
System.out.println(Arrays.toString(array)); // [a, ZZZ, c]
final var list = List.of(array);
System.out.println(list); // [a, ZZZ, c]
list.set(0, "YYY"); // UnsupportedOperationException
関連記事
- API 使用例