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個分続く
...

記述が大変なだけではなく、applemelon といった変数名を考えるのも大変ですね。
また、これだけの量を手作業で書いていくと、うっかり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

In the Java programming language, arrays are objects (§4.3.1), are dynamically created, and may be assigned to variables of type Object (§4.3.2). All methods of class Object may be invoked on an array.

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は注意

配列の equalshashCode は、配列の内容を考慮しません。

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

関連記事

ページの先頭へ