第7講・STEP 02

配列と参照型変数

テストの結果、Aさんは80点、Bさんは65点、Cさんは70点、Dさんは95点でした。何のテストやらよく分かりませんが、この点数を管理するプログラムを作りたいと思います。

配列とは?

class名は簡単に「Scores」でいきます。お決まりのmainメソッドを書いてから、その中でまずはAさんからDさんまでそれぞれの点数を入れる変数を用意します。

Scores.java

public class Scores {
    public static void main(String[] args) {
        int scoreA;
        int scoreB;
        int scoreC;
        int scoreD;
    }
}

こういう同じ変数型を複数、宣言するのってなんか面倒くさいですよね。そんな時は、以下のようにまとめて宣言することもできます。

Scores.java

int scoreA, scoreB, scoreC, scoreD;

このように変数名をコンマで区切って続けて書くと全部int型変数と認識してくれます。変数型を何回も書かなくていい分ちょっと楽ですね。

けど、もし点数を30人分入力するとなるとどうでしょう?30個も変数を用意しないと駄目ですよね。上記のやり方では変数型は1回で済みますが、変数名は30個用意しなくてはいけません。それではさすがに大変です。そういう時に使えるのが配列です。

配列を使えば、同じ型の要素をまとめて、一つの配列変数を通して扱うことができます(要素の型は共通です)。そうすることで一人一人の点数を格納する変数を宣言しなくても、一つの配列変数の宣言だけで全員分の点数を順番にその配列に格納できるので楽ちんです。

変数の宣言が楽になるのは確かですが、もちろんそれだけの為に配列を使うわけではありません。配列を使うことでプログラムの幅がぐっと広がっていろんな事が出来るようになるので、使い方や使い所を掴んで下さい。

配列の宣言と初期化

というわけで、配列を使ってやっていくので、さきほどのintの宣言文を書き換えます。配列変数も普通の変数と同じようにまず宣言する必要があります。

Scores.java

public class Scores {
    public static void main(String[] args) {
        int[] scores;
    }
}

このように、変数型の後ろに[ ]をつけると、その「変数型の配列」という意味になります。scoresは配列変数の名前です(複数形の単語にすると分かりやすくてgood)。

普通の変数の宣言と形は全く同じですね。これでint配列を参照するscoresという名前の変数を用意したわけです。配列本体との関係は、最後の「参照型変数」で詳しく見ていきます。

続いて初期化です。今回はnewを使って配列を作るので、その配列にいくつの要素を入れるのかを書く必要があります。いくつ入れるかまだ分からないので後で適当に足していきます、なんていう使い方はできません。以下の形で配列の要素数について記述します。

(mainメソッドの中のみ表記)

Scores.java

int[] scores;
scores = new int[4];

「new」は、新しい配列やオブジェクトを作るときに使うキーワードで、非常に重要な役割を果たします。今のところは「新しく作る」みたいな意味でとらえていて下さい。これで初期化は完了です。

・・完了といってもこの段階では箱を4つ用意しただけで箱の中身(int変数)については何の値も入れていないですよね。これで初期化と言えますでしょうか??mainメソッド内のローカル変数では何も値を入れずに(初期化せずに)読み出そうとするとコンパイルエラーになりましたよね。

実はnewした時点で各要素は勝手に全て0で初期化されているんです。つまり、「new int[4]」で0が入ったint変数を4つ用意しているわけです。
ちなみに配列も、普通の変数と同じく「宣言と初期化」は同時にできますので、縮めようと思えば、

Scores.java

int[] scores = new int[4];

これでもOKです。

配列の各要素に値を代入

配列の宣言と初期化によって、0が入った4つの箱(要素)が用意できました。全部0じゃ意味ないので用意した4つの要素に値を代入します。一つずつ入れていきましょう。

(mainメソッドの中のみ表記)

Scores.java

int[] scores = new int[4];

scores[0] = 80;
scores[1] = 65;
scores[2] = 70;
scores[3] = 95;

このように「配列変数名[通し番号]」の形で、用意した4つの要素のうちの一つを指し示すことができます。一つの要素を指し示してしまえば一つのint型変数と同じように扱えて、上記のように普通に値を代入できます。

気をつけないといけないのは、配列の1番目の要素の通し番号は1番ではなく0番だということです。これは覚えてください。配列の通し番号の最初は0番です。

配列の個数が4つならば、通し番号は0から3までということです。4つの要素からなる配列を作って4番目の要素に値を代入しようとして、

Scores.java

int[] scores = new int[4];
scores[4] = 80;

こうしてしまうと、アウトです。「scores[4]」というのは5番目の要素を指してしまいます。箱は4つしか用意していないので5番目の箱はありませんってことでエラーになります。これをやってしまうとコンパイルは通してくれるのですが、実行すると「例外」というのが投げられてプログラムが止まります(この例ではArrayIndexOutOfBoundsException。「例外」についてはまたの機会に)。
 慣れない内は要注意です。

各要素の値を指定する初期化

値を代入する作業は正直、面倒ですね。「配列なんて面倒なだけじゃないか」とお嘆きの方に朗報です。実はわざわざ一つずつ代入しなくても、一発で各要素に値を入れて初期化してしまう方法もあります。こんな風に書きます。

Scores.java

int[] scores;
scores = new int[]{80, 65, 70, 95};

このように「new int[]」の後ろに「{ }」をつけ足してその中に各要素の値を順番に「,」で区切りながら書いていきます。楽ちんですね。ただしこのやり方では、さっきの「new int[4]」のようにいくつ要素を用意するかの数字を書いてはいけません。じゃあどうやって要素の数を指定するのかと言うと、{ }の中に入れた要素の数がそのままこの配列の要素数になります。なので、5つの要素を用意したいが値を入れるのは4つだけにするなんてことはできません。

ここで、宣言の後で代入するときはnew int[]を省略できません。宣言と代入を分ける場合は、次の2行で書きます。

Scores.java

int[] scores;
scores = new int[]{80, 65, 70, 95};

一方、宣言と初期化を一緒にする場合だけ、new int[]を省略して、こんな風に書くことができます。

Scores.java

int[] scores = {80, 65, 70, 95};

短く書けると楽ですね。ただし、宣言と同時だから省略できるという点は忘れないで下さい。構文を分かった上で使うようにしましょう。

では、プログラムの方も値を指定した初期化の仕方に書き換えておきます。

(mainメソッドの中のみ表記)

Scores.java

int[] scores = new int[]{80, 65, 70, 95};

たった一行になってしまいました。ちょっと配列のありがたみが分かってきましたね。

配列を出力する

では配列として保持したこの4つの値、80、65、70、95を順番に出力していきます。値を代入した時と同じで「配列変数名[通し番号]」の形で各要素にアクセスできます。

(mainメソッドの中のみ表記)

Scores.java

int[] scores = new int[]{80, 65, 70, 95};

System.out.println("Aさんの点数:" + scores[0] + "点");
System.out.println("Bさんの点数:" + scores[1] + "点");
System.out.println("Cさんの点数:" + scores[2] + "点");
System.out.println("Dさんの点数:" + scores[3] + "点");

これで実行すると、

実行結果
Aさんの点数:80点
Bさんの点数:65点
Cさんの点数:70点
Dさんの点数:95点

こうなります。そのまんまですね。面白くもなんともなくて申し訳ないです。

配列を使う上での諸注意

「[ ]」のつける場所なんですが、これが慣れない内は非常に混乱します。よく見ると変数型の後ろにつけたり、配列変数名の後ろにつけたりしてますよね。

Scores.java

int[] scores;
scores = new int[4];
scores[0] = 80;

これは覚えてしまいましょう。
・newする時は変数型の後ろ
・通し番号を入れる時は変数名の後ろ
です。あともう一つ、宣言する時です。宣言する時は実はどっちでもいいんです。

Scores.java

int[] scores;
int scores[];

この2行は書き方の比較です。同じ場所に両方を書くと同じ名前の変数を二度宣言することになるので、使うときはどちらか一方だけを書いてください。

どちらでもOKなので、宣言については自分で分かりやすい、使いやすい方にしたらいいと思います。

参照型変数

さて、最後にもう一度、初期化のところを見てほしいのですが、

Scores.java

int[] scores;     //①
scores = new int[4];//②

これは具体的に何をしているかと言うと、

①配列を入れるscoresという名前の箱を用意する。
②intを入れる箱を4つ用意して、それをscoresという箱の中に入れる(代入する)。

・・・これちょっと不思議じゃないですか?
 まずscoresという箱を作ってから、その箱の中に後から別の箱を4つも持ってきて入れろと?4つならまだしも、もし100個も持って来られたら果たして入るのだろうか・・。そう考えると、初めに配列を格納する箱(scores)を用意する時、一体どんな大きさの箱を用意すればいいのか分かりませんよね。あくまで比喩ですが、一旦用意した箱を後から大きくしたりはできないですし、そもそも箱の中に他の箱を入れるなんてことはできません。けど、このソースコードを見る限り箱の中に4つの箱をぶち込んでいるように見えますよね?一体どういうこと?

説明します。

Scores.java

scores = new int[4];

「new int[4]」で、int型変数(intを入れる箱)を4つ確かに作っているのですが、箱を作った後scoresに代入されるのは、4つの箱自体ではありません。

配列の要素(intを入れる箱4つ)は作った場所に置いたまま、その箱が置いてある場所を示す値が、配列変数scoresに代入されます。例えて言うなら、場所を示す番地が書かれた紙切れ一枚をscoresという箱に入れているようなものです。

それなら箱100個であろうとも、急に後から言われようとも代入できますよね。箱が何個であってもscoresに入れるのは紙切れ一枚で済むんですから(あくまで比喩です)。

このように変数(箱)の中に、配列の各要素の値そのものではなく、「値を入れた箱の場所」を示す参照値(番地みたいなもの)が入っている変数を、参照型変数と言います。

配列変数は参照型変数の一種と言えます。それに対して、基本データ型変数は、用意した箱に値そのものが入っています。だから基本データ型変数は型の種類によってそれぞれ箱の大きさが決まっているんです(bit数)。たとえばintには表せる整数の範囲があり、その範囲外の整数リテラルをそのまま代入するとコンパイルエラーになります。ただし、計算結果が範囲を超えた場合まで必ずコンパイルエラーになるわけではありません。intの整数演算ではオーバーフローが起こることがあります。

さてここで問題です!
 以下のプログラムで一体何が出力されるでしょうか?これは参照型変数と基本データ型変数の大きな違いが分かるプログラムです。ゆっくり一行ずつ理解していけば全然難しくはないので、何が出力されるか自分で考えてみてください。

Test.java

public class Test {
    public static void main(String[] args) {
        /*問1*/
        int[] a = new int[]{1, 2, 3};//①
        int[] b = a;               //②
        b[0] = 5;                  //③
        System.out.println(a[0]);

        /*問2*/
        int c = 1;                 //④
        int d = c;                 //⑤
        d = 5;                     //⑥
        System.out.println(c);
    }
}

問1
①int配列aを宣言、要素は1,2,3。
②int配列bを宣言、そこに配列変数aを代入。ここがポイント!aには何が入っていたんでしたか?配列そのものではなく、配列を置いてある場所を示した紙切れです。紙切れに書いてある番地(値)を代入したんです。つまりこれでbも同じ番地が書かれた紙切れのコピーを持つことになりました。つまり同じ配列を参照するようになったんです。
③b[0]に5を代入。

さて、a[0]の値は何でしょうか??

問2
④int変数cを宣言し、そこに1を代入。
⑤int変数dを宣言し、そこにcを代入。cに入っているのは値そのものです。なので値そのものをdに代入しています。これでdはcと同じ値を持つことになりました。
⑥dに5を代入。

さて、cの値は何でしょうか??

分かりますでしょうか?本気で考えてみて下さい。参照型変数の仕組みを理解している人は、この問題が簡単に解けると思います。

答え合わせはご自身で実行してもらいたいところですが、それではさすがに不親切だと思うので、実行結果を載せておきます。

実行結果
5
1

どうですか?正解していましたか?間違っていた人はこのプログラムがしていることを一行ずつ順を追って理解していってください。

配列の基本的な使い方、参照型変数というものの仕組みがちょっとは掴めましたでしょうか?(参照型についてのもうちょっと詳しい説明→基本データ型変数と参照型変数)

次回は繰り返し処理for文を使って、もうちょっと複雑なプログラムを作りたいと思います。

目次へ

質問・コメント

読み込み中

この記事についての質問や、学習して気づいたことをお寄せください。

コメントを書く

投稿フォームを準備しています。

コメントと一緒に公開される名前です。
3,000文字以内。パスワードなどの個人情報は書き込まないでください。

コメントは送信後すぐに公開されます。

この記事のコメント

旧サイトに寄せられたコメントも当時の投稿内容のまま掲載しています。現在の記事では変更している内容や、旧環境についてのやり取りも含まれます。

コメントを読み込んでいます。