
Java道|オブジェクト配列のしくみとポイント
隊士を1人ずつ管理する段階から、隊をまとめて扱う段階へ。
オブジェクト配列を理解すると、複数のオブジェクトを順番に操作できるようになる。
ここまでで、クラスからオブジェクトを作り、そのオブジェクトを変数で扱う方法を学んできました。
たとえば、1人の隊士を表す CorpsSwordsman オブジェクトを作り、その隊士の剣技の強さや呼吸の力を設定することができました。
しかし、実際のプログラムでは、1人だけを扱えばよい場面ばかりではありません。
鬼滅の刃の世界で考えると、任務に参加する隊士が1人だけとは限りません。
水月、炎真、蒼真のように、複数の隊士をまとめて管理したい場面があります。
1人目は mizuki。
2人目は enma。
3人目は soma。
というように、すべて別々の変数で管理することもできます。
でも人数が増えてくると、変数名も増え、同じ処理を何度も書くことになり、だんだん扱いにくくなります。
そこで便利なのが、オブジェクト配列です。
オブジェクト配列を使うと、複数のオブジェクトを1つの配列にまとめて管理できます。
さらに、for 文と組み合わせれば、全員の状態を順番に設定したり、表示したりできます。
鬼滅の刃でたとえると、オブジェクト配列は「隊士を並べる控え席」です。
ただし、ここで大切なのは、席を用意しただけでは隊士本人はまだ存在しないという点です。
まず席を用意する。
そのあとで、各席に隊士を1人ずつ配置する。
この2段階をしっかり分けて考えることが、オブジェクト配列を理解する一番大事なポイントです。
配列はオブジェクトもまとめて扱える
配列というと、最初は int 型の点数や double 型の数値をまとめて扱うイメージが強いかもしれません。
たとえば、点数を5個まとめて扱うなら、次のような形です。
int[] scores;
scores = new int[5];これは、int 型の値を5個分まとめて入れられる配列を用意する書き方です。
同じように、オブジェクトも配列でまとめて扱えます。
たとえば、CorpsSwordsman オブジェクトを3人分まとめて扱いたい場合は、次のように書けます。
CorpsSwordsman[] swordsmen;
swordsmen = new CorpsSwordsman[3];int の代わりに、クラス名 CorpsSwordsman を使っているところがポイントです。
| 配列の種類 | 書き方 | 扱うもの |
|---|---|---|
| int 型の配列 | int[] scores | int 型の値 |
| CorpsSwordsman 型の配列 | CorpsSwordsman[] swordsmen | CorpsSwordsman オブジェクトへの参照 |
ここで少し注意が必要です。
CorpsSwordsman[] swordsmen は、CorpsSwordsman オブジェクトそのものを直接並べるというより、CorpsSwordsman オブジェクトを指す参照を並べる配列です。
鬼滅の刃でたとえると、隊士本人を箱の中にぎゅうぎゅう詰めにするのではありません。
隊士を指す札を、順番に並べて管理するようなイメージです。
配列を作っただけでは、まだ隊士は存在しない
オブジェクト配列で特に重要なのが、この点です。
CorpsSwordsman[] swordsmen;
swordsmen = new CorpsSwordsman[3];この2行を書いただけでは、隊士オブジェクトが3人作られたわけではありません。
ここで作られているのは、CorpsSwordsman 型の参照を3つ入れられる配列です。
つまり、3人分の席を用意しただけです。
| 書き方 | 起きていること |
|---|---|
| CorpsSwordsman[] swordsmen; | 配列変数を宣言する |
| new CorpsSwordsman[3] | CorpsSwordsman 型の参照を3つ入れられる配列を作る |
| new CorpsSwordsman() | 実際の隊士オブジェクトを1人作る |
鬼滅の刃でたとえると、new CorpsSwordsman[3] は、3人分の控え席を用意することです。
でも、その席にはまだ誰も座っていません。
つまり、配列の各要素はまだ空の状態です。
この状態で、いきなり swordsmen[0].show() のようにメソッドを呼び出そうとすると、参照先がないため問題が起きます。
席はある。
でも隊士はいない。
この感覚をしっかり持つことが大切です。
オブジェクト配列では2つのnewを区別する
オブジェクト配列では、new が2種類出てきます。
ここがとても混乱しやすいところです。
| 書き方 | 役割 | 鬼滅の刃でたとえると |
|---|---|---|
| new CorpsSwordsman[3] | 配列を作る | 3人分の控え席を用意する |
| new CorpsSwordsman() | オブジェクトを作る | 実際の隊士を1人登場させる |
同じ new でも、作っているものが違います。
new CorpsSwordsman[3] は、配列を作っています。
new CorpsSwordsman() は、隊士オブジェクトを作っています。
この2つがそろって、はじめてオブジェクト配列として使える状態になります。
オブジェクト配列で必要な2つの作業
オブジェクトを配列で扱うには、次の2つの作業が必要です。
| 手順 | 内容 | 鬼滅の刃でのイメージ |
|---|---|---|
| 1 | 配列を準備する | 隊士を並べる控え席を用意する |
| 2 | 各要素にオブジェクトを作って代入する | 各席に実際の隊士を座らせる |
この2つを分けて考えると、オブジェクト配列はかなり分かりやすくなります。
配列を作っただけでは、各要素はまだオブジェクトを指していません。
各要素に new CorpsSwordsman() を代入して、はじめてその要素からメソッドを呼べるようになります。
オブジェクト配列を確認する
実際のプログラムで確認していきましょう。
ファイル名:Sample9.java
class CorpsSwordsman
{
private int strength;
private double breathingPower;
public CorpsSwordsman()
{
strength = 0;
breathingPower = 0.0;
System.out.println("隊士を作成しました。");
}
public void setSwordsman(int s, double b)
{
strength = s;
breathingPower = b;
System.out.println("剣技の強さを" + strength + "に呼吸の力を" + breathingPower + "にしました。");
}
public void show()
{
System.out.println("剣技の強さは" + strength + "です。");
System.out.println("呼吸の力は" + breathingPower + "です。");
}
}
class Sample9
{
public static void main(String[] args)
{
CorpsSwordsman[] swordsmen;
swordsmen = new CorpsSwordsman[3];
for(int i = 0; i < swordsmen.length; i++){
swordsmen[i] = new CorpsSwordsman();
}
swordsmen[0].setSwordsman(9000, 20.5);
swordsmen[1].setSwordsman(8500, 30.5);
swordsmen[2].setSwordsman(7000, 40.5);
for(int i = 0; i < swordsmen.length; i++){
swordsmen[i].show();
}
}
}このプログラムでは、CorpsSwordsman オブジェクトを3人分まとめて扱っています。
1人ずつ別々の変数を用意するのではなく、swordsmen という配列にまとめています。
| 配列要素 | 役割 |
|---|---|
| swordsmen[0] | 1人目の隊士を指す |
| swordsmen[1] | 2人目の隊士を指す |
| swordsmen[2] | 3人目の隊士を指す |
配列を使うことで、複数の隊士を順番に扱いやすくなります。
最初のnewは配列を作っている
まず注目するのは、次の部分です。
CorpsSwordsman[] swordsmen;
swordsmen = new CorpsSwordsman[3];ここでは、CorpsSwordsman 型の配列を用意しています。
ただし、この時点では隊士オブジェクトはまだ作られていません。
この段階で用意されているのは、次の3つの要素です。
| 要素 | 状態 |
|---|---|
| swordsmen[0] | まだ隊士を指していない |
| swordsmen[1] | まだ隊士を指していない |
| swordsmen[2] | まだ隊士を指していない |
鬼滅の刃でたとえると、3つの控え席が用意されただけです。
まだ水月も炎真も蒼真も座っていません。
この状態では、swordsmen[0] から setSwordsman() や show() を呼び出すことはできません。
参照先の隊士がまだいないからです。
2回目のnewで実際の隊士を作る
次に出てくるのが、この for 文です。
for(int i = 0; i < swordsmen.length; i++){
swordsmen[i] = new CorpsSwordsman();
}ここで初めて、各配列要素に CorpsSwordsman オブジェクトを作って代入しています。
つまり、次の3行をまとめて書いているのと同じ考え方です。
swordsmen[0] = new CorpsSwordsman();
swordsmen[1] = new CorpsSwordsman();
swordsmen[2] = new CorpsSwordsman();この処理によって、各要素が実際の隊士オブジェクトを指すようになります。
| 処理 | 起きていること |
|---|---|
| swordsmen[0] = new CorpsSwordsman(); | 1人目の隊士を作って、0番に入れる |
| swordsmen[1] = new CorpsSwordsman(); | 2人目の隊士を作って、1番に入れる |
| swordsmen[2] = new CorpsSwordsman(); | 3人目の隊士を作って、2番に入れる |
鬼滅の刃でたとえると、用意した3つの控え席に、実際の隊士を1人ずつ座らせている状態です。
ここまで来て、ようやく swordsmen[0].setSwordsman(...) のように、各隊士に対してメソッドを呼び出せるようになります。
for文でまとめてオブジェクトを作れる
このプログラムでは、for 文を使って3人分の隊士をまとめて作っています。
for(int i = 0; i < swordsmen.length; i++){
swordsmen[i] = new CorpsSwordsman();
}ここで使っている swordsmen.length は、配列の要素数を表します。
今回の配列は new CorpsSwordsman[3] で作っているため、swordsmen.length は 3 です。
| i の値 | 実行される処理 |
|---|---|
| 0 | swordsmen[0] = new CorpsSwordsman(); |
| 1 | swordsmen[1] = new CorpsSwordsman(); |
| 2 | swordsmen[2] = new CorpsSwordsman(); |
このように、配列と for 文を組み合わせると、同じパターンの処理をまとめて書けます。
もし隊士が10人、20人と増えても、for 文の形は大きく変わりません。
人数分だけ同じ処理を繰り返せるのが、配列の強みです。
配列要素ごとに別々の状態を持てる
次に、それぞれの隊士に異なる値を設定しています。
swordsmen[0].setSwordsman(9000, 20.5);
swordsmen[1].setSwordsman(8500, 30.5);
swordsmen[2].setSwordsman(7000, 40.5);ここでのポイントは、同じ CorpsSwordsman クラスから作られた3人でも、それぞれ別々の状態を持てることです。
| 配列要素 | 剣技の強さ | 呼吸の力 |
|---|---|---|
| swordsmen[0] | 9000 | 20.5 |
| swordsmen[1] | 8500 | 30.5 |
| swordsmen[2] | 7000 | 40.5 |
鬼滅の刃でたとえると、同じ鬼殺隊士の設計図から生まれた隊士でも、剣技の強さや呼吸の力はそれぞれ違います。
配列でまとめて管理していても、各要素が指しているオブジェクトは別々です。
そのため、1人目、2人目、3人目で異なる値を持たせられます。
配列要素からメソッドを呼び出す
オブジェクト配列では、配列要素を通してメソッドを呼び出せます。
swordsmen[0].setSwordsman(9000, 20.5);
swordsmen[0].show();これは、swordsmen[0] が指している CorpsSwordsman オブジェクトに対して、setSwordsman() や show() を呼び出しているという意味です。
| 書き方 | 意味 |
|---|---|
| swordsmen[0].setSwordsman(...) | 0番の要素が指す隊士に値を設定する |
| swordsmen[1].show() | 1番の要素が指す隊士の状態を表示する |
| swordsmen[i].show() | i番の要素が指す隊士の状態を表示する |
鬼滅の刃でたとえると、0番席の隊士に訓練値を設定したり、1番席の隊士に状態報告をさせたりしているようなものです。
ただし、ここでも大切なのは、配列要素がオブジェクトを指している必要があるという点です。
まだ new CorpsSwordsman() を代入していない要素からメソッドを呼ぶことはできません。
for文で全員の情報を表示する
最後に、もう一度 for 文を使って、全員の情報を順番に表示しています。
for(int i = 0; i < swordsmen.length; i++){
swordsmen[i].show();
}このループによって、次のように処理が進みます。
| i の値 | 実行される処理 |
|---|---|
| 0 | swordsmen[0].show(); |
| 1 | swordsmen[1].show(); |
| 2 | swordsmen[2].show(); |
これにより、1人目、2人目、3人目の状態を順番に表示できます。
もし配列を使わなければ、次のように1つずつ書く必要があります。
swordsmen[0].show();
swordsmen[1].show();
swordsmen[2].show();3人ならまだよいですが、10人、30人と増えていくと大変です。
配列と for 文を使えば、人数が増えても同じ形で処理できます。
鬼滅の刃でたとえると、控え席に並んだ隊士へ順番に声をかけ、状態報告をしてもらう流れです。
実行の流れを整理する
Sample9.java の処理全体を流れで整理すると、次のようになります。
| 順番 | 処理 | 内容 |
|---|---|---|
| 1 | CorpsSwordsman[] swordsmen; | 配列変数を宣言する |
| 2 | new CorpsSwordsman[3] | 3人分の参照を入れる配列を作る |
| 3 | for 文で new CorpsSwordsman() | 各要素に隊士オブジェクトを作る |
| 4 | setSwordsman() | 各隊士に異なる値を設定する |
| 5 | for 文で show() | 全員の状態を順番に表示する |
実行すると、まずコンストラクタが3回呼び出されます。
つまり、隊士が3人作られます。
そのあと、各隊士に剣技の強さと呼吸の力が設定され、最後に全員の状態が順番に表示されます。
この流れを見ると、オブジェクト配列は「複数のオブジェクトをまとめて作り、まとめて扱う」ための仕組みだと分かります。
図:配列を作っただけではオブジェクトはまだない
↓クリックすると拡大表示されます。

この図が示していること
この図では、new CorpsSwordsman[3] によって配列が作られた直後の状態を表しています。
swordsmen[0]、swordsmen[1]、swordsmen[2] という3つの要素はあります。
しかし、それぞれの要素にはまだ CorpsSwordsman オブジェクトが入っていません。
ここから分かるのは、オブジェクト配列では、配列を作ることと、オブジェクトを作ることは別の作業だということです。
配列を作っただけでは、隊士を3人作ったことにはなりません。
図:各要素にオブジェクトを作って代入する
↓クリックすると拡大表示されます。

この図が示していること
この図では、swordsmen[0]、swordsmen[1]、swordsmen[2] の各要素が、それぞれ別の CorpsSwordsman オブジェクトを指している状態を表しています。
swordsmen[0] は、剣技の強さ 9000、呼吸の力 20.5 の隊士を指しています。
swordsmen[1] は、剣技の強さ 8500、呼吸の力 30.5 の隊士を指しています。
swordsmen[2] は、剣技の強さ 7000、呼吸の力 40.5 の隊士を指しています。
ここから分かるのは、配列でまとめて管理していても、各要素が指すオブジェクトは別々に存在し、それぞれ独自の状態を持てるということです。
オブジェクト配列で特に注意したいこと
オブジェクト配列では、次の点に注意すると理解しやすくなります。
| 注意点 | 内容 |
|---|---|
| 配列を作っただけではオブジェクトはない | new CorpsSwordsman[3] は席を作るだけ |
| 各要素に new が必要 | swordsmen[i] = new CorpsSwordsman(); で隊士を作る |
| 配列要素は参照を持つ | オブジェクト本体ではなく、オブジェクトへの参照 |
| 参照先がない要素からメソッドは呼べない | オブジェクトを代入してから使う |
| length を使うと要素数分だけ処理できる | for 文と相性がよい |
特に大切なのは、new CorpsSwordsman[3] と new CorpsSwordsman() の違いです。
この2つを同じものとして見てしまうと、オブジェクト配列で混乱しやすくなります。
鬼滅の刃でたとえるなら、
new CorpsSwordsman[3] は3人分の席を用意すること。
new CorpsSwordsman() はその席に座る隊士を1人作ること。
この違いです。
配列は便利だが要素数はあとから簡単には変えられない
配列は、複数のオブジェクトを順番に扱うときにとても便利です。
ただし、配列には大切な特徴があります。
最初に要素数を決めて作る必要があるという点です。
今回なら、次のように3人分として作っています。
swordsmen = new CorpsSwordsman[3];この配列は、基本的に3つの要素を持つ配列です。
あとから自由に4人目、5人目を追加していくのは得意ではありません。
| 仕組み | 特徴 |
|---|---|
| 配列 | 要素数を最初に決める |
| コレクション | 要素数の増減に対応しやすい |
ここでは、まず配列でオブジェクトをまとめて扱う考え方をしっかり押さえることが大切です。
人数があらかじめ決まっている場合には、配列はとても分かりやすく使えます。
鬼滅の刃でたとえると、3人任務と決まっているなら3席の控え席を用意すれば十分です。
でも、任務の途中で参加人数がどんどん増えるような場面では、別の管理方法がほしくなります。
オブジェクト配列を使うと何が便利なのか
オブジェクト配列の便利さは、複数のオブジェクトをまとめて同じ処理にかけられるところです。
| やりたいこと | 配列を使う利点 |
|---|---|
| 複数の隊士を作る | for 文でまとめて作れる |
| 複数の隊士を表示する | for 文で順番に show() を呼べる |
| 順番に管理する | swordsmen[0]、swordsmen[1] のように番号で扱える |
| 人数が増えても対応しやすい | 変数名を大量に増やさなくてよい |
| 同じ処理を繰り返せる | ループと組み合わせやすい |
1人ずつ別々の変数で扱う場合は、人数が増えるほどコードが長くなります。
CorpsSwordsman mizuki;
CorpsSwordsman enma;
CorpsSwordsman soma;しかし配列なら、1つの変数 swordsmen でまとめて扱えます。
CorpsSwordsman[] swordsmen = new CorpsSwordsman[3];鬼滅の刃でたとえると、隊士を1人ずつ別々の巻物で管理するのではなく、隊士名簿としてまとめて管理するようなものです。
ここで押さえておきたい重要ポイント
最後に、オブジェクト配列で大切なポイントを整理します。
| ポイント | 内容 |
|---|---|
| オブジェクトも配列でまとめて扱える | クラス名[] の形で配列を作れる |
| 配列を作っただけではオブジェクトはない | new CorpsSwordsman[3] は参照の箱を作るだけ |
| 各要素にオブジェクトを代入する必要がある | new CorpsSwordsman() を各要素に入れる |
| 配列要素はオブジェクトへの参照を持つ | 要素そのものがオブジェクト本体ではない |
| for 文と相性がよい | 作成、表示などをまとめて行える |
| new 配列 と new オブジェクト は役割が違う | 席を作る処理と、隊士を作る処理は別 |
鬼滅の刃でたとえると、オブジェクト配列は隊士を並べる控え席です。
まず、任務に参加する隊士を並べる席を用意します。
そのあとで、各席に実際の隊士を1人ずつ配置します。
そして、配列番号を使って、1人目、2人目、3人目を順番に扱います。
この感覚がつかめると、オブジェクトを1人ずつ扱う段階から、複数のオブジェクトをまとめて管理する段階へ進めます。
オブジェクト配列は、Javaのプログラムを少し大きくしていくうえで、とても大切な一歩です。
