PR

Java:配列[array]とは?使い方の基本をわかりやすく3分で

Java

配列[Array]は、同じデータ型の複数の値を1つの変数に格納するためのデータ構造です。Javaにおける配列は固定長で、一度作成するとそのサイズを変更することはできないという特徴を持ちます。配列の各要素には、インデックス(添え字)を使ってアクセスし、インデックスは0から始まり、配列の長さ-1までの整数で表します。

java 配列
図1:Javaの配列

整数を格納するための配列を例にして考えると、配列を使うことで複数の整数値を1つの変数にまとめて管理することができ、個々の値にアクセスしたり、変更したりするのが簡単になります。

// 整数型の配列を宣言して初期化
int[] numbers = {1, 2, 3, 4, 5};

// 配列の要素にアクセス
System.out.println(numbers[0]); // 出力: 1
System.out.println(numbers[4]); // 出力: 5

↑は、numbersという配列に5つの整数値を格納。numbers[0]は配列の最初の要素(1)を、numbers[4]は配列の最後の要素(5)を示しています。

配列を使うことで、複数の関連するデータを効率的・操作することが可能になります。この特性を理解して、適切な場面で配列を活用できるようになりましょう。

スポンサーリンク

配列の宣言と初期化

Javaで配列を使うためには、①配列の宣言、②初期化というステップを踏む必要があります。ここでは、配列の宣言と初期化の基本的な方法についてご説明します。

配列の宣言

配列を宣言するというのは、配列の名前とそのデータ型をJavaに教えるということ。例えば、整数型の配列を宣言するには次のように書きます。

int[] numbers;

int はデータ型を、[] は配列であることを示しており、numbers が配列の名前です。

配列の初期化

配列を宣言しただけでは、実際にデータを格納するためのメモリが割り当てられていません。データを格納するためには配列の初期化を行う必要があります。初期化にはいくつかの方法があります。

パターン1 サイズを指定して初期化
配列のサイズ(要素数)を指定して初期化する方法です。この方法では、配列の各要素はデフォルト値(整数の場合は0)で初期化されます。以下の方法で、5つの整数を格納できる配列 numbers が作成されます。

// 配列の宣言
int[] numbers;

// 配列の初期化
numbers = new int[5];

パターン2 宣言と同時に初期化
配列を宣言すると同時に、初期化する方法もあります。この方法では、配列の各要素を具体的な値で初期化。以下の通りに記述すると、要素として1から5までの値を持つ配列 numbers が作成されます。

int[] numbers = {1, 2, 3, 4, 5};

配列の宣言&初期化自体は難しくありません。以下のコードだけ頭に入っていればOK。

// 配列の宣言
int[] scores;

// 配列の初期化
scores = new int[3]; // 3つの要素を持つ配列を初期化

// 宣言と同時に初期化
int[] ages = {18, 21, 25}; // 3つの値を持つ配列を作成
配列の宣言方法は実はいろんなパターンがある

Javaの配列宣言では、[] を型の後ろに書く方法と、変数名の後ろに書く方法の2種類があります。どちらでもコンパイルは通りますが、可読性やチームのコーディング規約によって推奨される方法が異なる場合があります。

型の後ろに [] を付ける例

int[] arrayA = new int[5];
String[] names = {"Alice", "Bob", "Carol"};

このように書くのが一般的で、配列であることが直感的にわかりやすいとされています。

変数名の後ろに [] を付ける例

int arrayB[] = new int[5];
String names2[] = {"Dave", "Ellen", "Frank"};

C言語由来の書き方で、Javaでも動作は同じですが、推奨されないことが多いです。

同じ行で複数の変数を宣言する場合の注意

int[] x, y;  // xもyもint配列
int x2[], y2; // x2はint配列、y2はint型の変数

1行で複数変数を宣言するとき、どこに [] を付けるかで配列なのか通常の変数なのかが変わるので注意が必要です。

まとめとして、[] は型の直後に書くのがわかりやすく、複数変数を1行で宣言するよりも1変数1行で宣言するほうが混乱を避けられます。

多次元配列

Javaでは、配列の中に配列を持つことができるため、複数の次元を持つ多次元配列を作成できます。最も一般的な多次元配列は二次元配列で、これは表やマトリックスのようなデータを扱うのに便利です。

二次元配列の宣言と初期化

二次元配列を宣言するには、次のように書きます。int はデータ型を、[][] は二次元配列であることを示しています。

int[][] matrix;

二次元配列についても初期化が必要です。考え方は通常の配列と同様です。

// 二次元配列の宣言
int[][] matrix;

// 二次元配列の初期化
matrix = new int[2][3]; // 2行3列の二次元配列を作成

// 宣言と同時に初期化
int[][] scores = {
    {80, 90, 100},
    {70, 85, 95}
};

多次元配列を使うことで、表形式のデータや複雑なデータ構造を効率的に管理できるようになります。例えば、表形式のデータ(成績表など)やグリッド状のデータ(画像のピクセル情報など)を扱う場合に便利です。

二次元配列だけでなく、三次元以上の配列も作成できますが、使用する際には配列の次元が増えるほど管理が複雑になるため、適切な使い分けが必要です。

配列の要素へのアクセス

続けて、作成した配列に対する操作系を諸々ご説明します。基本的にどのような操作でも共通して、配列内の各要素にアクセスするためにはインデックス(添え字)を使用する、というのが基本ルールです。

要素の取得

配列の要素を取得するには、配列名の後に角括弧 [] を使い、取得したい要素のインデックスを指定します。

int[] numbers = {10, 20, 30, 40, 50};
int firstElement = numbers[0]; // 最初の要素(10)
int thirdElement = numbers[2]; // 3番目の要素(30)

要素の設定

配列の要素を設定するには、同じく角括弧 [] を使って要素のインデックスを指定し、新しい値を代入します。

int[] numbers = {10, 20, 30, 40, 50};
numbers[1] = 25; // 2番目の要素を25に設定
numbers[4] = 55; // 5番目の要素を55に設定

二次元配列の要素へのアクセスと設定

二次元配列の要素にアクセスするには、行と列のインデックスを指定します。

// 宣言と同時に初期化
int[][] scores = {
    {80, 90, 100},
    {70, 85, 95}
};

// 要素へのアクセス
int firstScore = scores[0][0]; // 1行目の1列目の要素(80)
scores[1][2] = 99; // 2行目の3列目の要素を99に設定

配列の長さの取得

列の長さを取得するために length プロパティを使用します。配列の長さは、その配列に含まれる要素の数を示します。

int[] numbers = {10, 20, 30, 40, 50};
int length = numbers.length;
System.out.println("Array length: " + length); // 出力: Array length: 5

配列のループ処理

配列の全ての要素にアクセスするために、forループや拡張forループ(foreachループ)を使います。

サンプルコード1 forループ

int[] numbers = {10, 20, 30, 40, 50};
System.out.println("Array elements (for loop):");
for (int i = 0; i < numbers.length; i++) {
    System.out.println("Element at index " + i + ": " + numbers[i]);
}
// 出力:
// Element at index 0: 10
// Element at index 1: 20
// Element at index 2: 30
// Element at index 3: 40
// Element at index 4: 50

サンプルコード2 拡張forループ

int[] numbers = {10, 20, 30, 40, 50};
System.out.println("Array elements (foreach loop):");
for (int number : numbers) {
    System.out.println("Element: " + number);
}
// 出力:
// Element: 10
// Element: 20
// Element: 30
// Element: 40
// Element: 50

参考 Javaのfor文:繰り返し処理の基本から応用までを3分で解説

配列のコピー

配列の内容を別の配列にコピーするには、Arrays.copyOf メソッドを使用します。このメソッドは、元の配列の内容を指定された長さの新しい配列にコピーします。

int[] numbers = {10, 20, 30, 40, 50};
int[] numbersCopy = Arrays.copyOf(numbers, numbers.length);
System.out.println("Copied array elements:");
for (int number : numbersCopy) {
    System.out.println("Element: " + number);
}
// 出力:
// Element: 10
// Element: 20
// Element: 30
// Element: 40
// Element: 50

参考 Java:メソッドの定義と呼び出しのコツを3分で解説

配列のソート

配列の要素を昇順に並べ替えるには、Arrays.sort メソッドを使用します。

int[] numbers = {50, 20, 40, 10, 30};
Arrays.sort(numbers);
System.out.println("Sorted array elements:");
for (int number : numbers) {
    System.out.println("Element: " + number);
}
// 出力:
// Element: 10
// Element: 20
// Element: 30
// Element: 40
// Element: 50

配列の検索

特定の要素が配列内に存在するかどうかを調べるには、Arrays.binarySearch メソッドを使用します。このメソッドを使用する前には、事前に配列をソートしておく必要があります。

int[] numbers = {10, 20, 30, 40, 50};
int index = Arrays.binarySearch(numbers, 30);
if (index >= 0) {
    System.out.println("Element found at index: " + index); // 出力: Element found at index: 2
} else {
    System.out.println("Element not found");
}

index = Arrays.binarySearch(numbers, 25);
if (index >= 0) {
    System.out.println("Element found at index: " + index);
} else {
    System.out.println("Element not found"); // 出力: Element not found
}

配列の長さの取得、配列のループ処理、配列のコピー、配列のソート、配列の検索について詳しく解説しました。これらの操作を理解することで、Javaの配列を効果的に利用できるようになります。

ここまでがJavaにおける配列の基本知識。ここからは、より実践に即した形でJavaの配列をより深く深堀して解説します。

Javaの「配列」をより本質的に理解する

そもそも配列とは何か?を改めて深堀します。

「配列」とは、同じ型の要素を連続的に並べて管理するための仕組みです。低レイヤー言語(C言語など)を学習済の方であれば、メモリ上に要素が連続して並んでいるイメージを持つことができるかと思います。一方、Javaでは配列自体がオブジェクトとして扱われ、要素の型や長さを保持します。

配列はオブジェクトである

配列はオブジェクトである――このフレーズは、初心者の方には少し抽象的に感じられるかもしれません。ここで、なぜ「ただのデータ型」ではなく「オブジェクト」であると言えるのか、その違いを平易な言葉で解説します。

配列にも「住所(参照)」がある

Javaにおいて、オブジェクトとは「newによってヒープ領域に作られた実体」を指します(参考 オブジェクトとは?)。クラスのインスタンスと同じように、配列も new int[5] のように書くと、Javaのヒープ領域に「配列オブジェクト」が確保され、そこへの参照(「住所」のようなもの)を変数が持つ仕組みです。

  • int x = 10; のようにプリミティブ型(int, doubleなど)を使うときは、その値を直接変数に格納します。
  • int[] arr = new int[5]; のように配列を使うときは、「配列の本体」はヒープ領域にあり、arr という変数は配列の本体がある「場所」を指し示すだけです。

「ただのデータ型」のように考えると、「配列変数に入っているのは配列そのもの」だと誤解しがちですが、実際には「配列そのものが置かれている場所への参照」が入っている点が大きな違いです。

length という情報を持っているという意味

配列はオブジェクトであるので、「要素数」や「要素の型」といった情報を内部に持つことができています。たとえば、arr.length という形で要素数を取得することができます。これは「配列オブジェクト」が自分が何個の要素を持っているのかを覚えているからです。

int[] arr = new int[5];
System.out.println(arr.length);  // 5 と出力

もし「配列」が本当にただの「数字のかたまり」や「型」だけであれば、こうしたメタ情報(要素数など)を持ち歩くことは難しいでしょう。オブジェクトとして扱われるからこそ、length という変数を備え、範囲外アクセスをチェックしたりできる仕組みが成り立っています。

instanceof 演算子や Object 型との関係

Javaのすべてのオブジェクトは、最終的に Object クラスを親に持つとされます。配列も例外ではなく、実は

int[] arr = new int[5];
System.out.println(arr instanceof Object); // true

のように、「配列は Object のインスタンス」として判定されます。これは、配列が特殊なクラスのインスタンスとして実装されていることを意味します。もし単なる「データ型」扱いであれば、instanceof Object が通ることはありません。

参考 インデックスの範囲チェック

Javaでは、配列のアクセス時に必ずインデックスが範囲内かどうかチェックされます。範囲外の場合(0 <= index < arr.length を外れる)は ArrayIndexOutOfBoundsException がスローされ、安全性が担保されています。

int[] arr = {1, 2, 3};
System.out.println(arr[3]);  // ArrayIndexOutOfBoundsException

参考 共変(Covariance)と型安全性

Javaの配列は「共変」という性質を持ち、例えば Number[] nums = new Integer[5]; のように、親クラス型の配列参照に子クラス型の配列オブジェクトを格納できます。しかし、実際の格納先は Integer[] なので、子クラスの要素以外を代入しようとすると実行時に ArrayStoreException が発生します。

Number[] nums = new Integer[5];
nums[0] = 10;      // IntegerなのでOK
nums[1] = 10.5;    // Doubleを代入しようとするとArrayStoreException

配列のまとめ

  • 配列はnewによって生成されるオブジェクト … 配列もクラスのインスタンスと同様に、ヒープ領域に配置され、その参照を変数が持ちます。
  • 要素数や型などを内部に保持length フィールドによる要素数の参照や、インデックスの境界チェックは、配列がオブジェクトとして情報を持っているから可能です。
  • コピーや参照のしくみがクラスのインスタンスと同じ … 配列変数に別の配列を代入すると「同じオブジェクト」を指すようになるなど、「ただのデータ型」とは扱いが異なります。

もし「配列はただのデータ型」と考えると、

  • 変数に格納されているのは「配列の中身」そのものだと思い込んでしまう
  • 配列を別変数に代入したら、配列が複製されたと思い込んでしまう
    などの誤解を招きやすいです。

しかし実際には、クラスのインスタンスと同じく、newによって作られた一つのまとまりを「アドレスを通じて参照している」状態です。こうした違いを理解すると、配列の挙動がより納得しやすくなるでしょう。

タイトルとURLをコピーしました