2019/02/06
sponsored link
配列とは?
テストの結果、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; } }
こういう同じ変数型を複数、宣言するのってなんか面倒くさいですよね。そんな時は、以下のようにまとめて宣言することもできます。
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という名前の箱を用意したわけです。
続いて初期化です。配列の初期化は普通の変数の初期化と違って、その配列にいくつの要素を入れるのかを書く必要があります。いくつ入れるかまだ分からないので後で適当に足していきます、なんていう使い方はできません。以下の形で配列の要素数について記述します。
(mainメソッドの中のみ表記)
int[] scores;
scores = new int[4];
「new」というのは演算子の一種で非常に重要な役割を果たします。今のところは「新しく作る」みたいな意味でとらえていて下さい。これで初期化は完了です。
・・完了といってもこの段階では箱を4つ用意しただけで箱の中身(int変数)については何の値も入れていないですよね。これで初期化と言えますでしょうか??基本データ型の変数では何も値を入れずに(初期化せずに)使うとコンパイルエラーになりましたよね。
実はnewした時点で各要素は勝手に全て0で初期化されているんです。つまり、「new int[4]」で0が入ったint変数を4つ用意しているわけです。
ちなみに配列も、普通の変数と同じく「宣言と初期化」は同時にできますので、縮めようと思えば、
int[] scores = new int[4];
これでもOKです。
check point
- 配列の宣言の形「変数型[ ] 配列変数名;」
- 配列の初期化の形「配列変数名 = new 配列型[要素数]」
- int配列の各要素は勝手に0で初期化される
- 普通の変数と同じで、宣言と初期化は一緒にしてもよい
配列の各要素に値を代入
配列の宣言と初期化によって、0が入った4つの箱(要素)が用意できました。全部0じゃ意味ないので用意した4つの要素に値を代入します。一つずつ入れていきましょう。
(mainメソッドの中のみ表記)
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番目の要素に値を代入しようとして、
int[] scores = new int[4]; scores[4] = 80;
こうしてしまうと、アウトです。「scores[4]」というのは5番目の要素を指してしまいます。箱は4つしか用意していないので5番目の箱はありませんってことでエラーになります。これをやってしまうとコンパイルは通してくれるのですが、実行すると「例外」というのが投げられてプログラムが止まります(「例外」についてはまたの機会に)。
慣れない内は要注意です。
check point
- 「配列変数名[通し番号]」でその配列内の要素を示す
- 通し番号は0番から始まる。
- 要素がi個ならば、通し番号は(i-1)番まで
各要素の値を指定する初期化
値を代入する作業は正直、面倒ですね。「配列なんて面倒なだけじゃないか」とお嘆きの方に朗報です。実はわざわざ一つずつ代入しなくても、一発で各要素に値を入れて初期化してしまう方法もあります。こんな風に書きます。
int[] scores;
scores = new int[]{80,65,70,95};
このように「new int[]」の後ろに「{ }」をつけ足してその中に各要素の値を順番に「,」で区切りながら書いていきます。楽ちんですね。ただしこのやり方では、さっきの「new int[4]」のようにいくつ要素を用意するかの数字を書いてはいけません。じゃあどうやって要素の数を指定するのかと言うと、{ }の中に入れた要素の数がそのままこの配列の要素数になります。なので、5つの要素を用意したいが値を入れるのは4つだけにするなんてことはできません。
もっと縮めてこんなのもありです。
int[] scores;
scores = {80,65,70,95};
ただ、ここまで省略してしまうと、ちょっと品がないような・・。特に初心者の方は楽だからという理由でとにかく短い書き方を選ぶのはよくないです。構文を分かった上で短く書きたい人の為の省略形だと思っておいて下さい。
では、プログラムの方も値を指定した初期化の仕方に書き換えておきます。
(mainメソッドの中のみ表記)
int[] scores = new int[]{80,65,70,95};
たった一行になってしまいました。ちょっと配列のありがたみが分かってきましたね。
check point
- 配列変数名 = new 変数型[ ]{要素1,要素2,要素3,・・・}で、要素の中身も一緒に初期化
- 配列変数名 = {要素1,要素2,要素3,・・・}も同じく
配列を出力する
では配列として保持したこの4つの値、80、65、70、95を順番に出力していきます。値を代入した時と同じで「配列変数名[通し番号]」の形で各要素にアクセスできます。
(mainメソッドの中のみ表記)
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] + "点");
これで実行すると、
こうなります。そのまんまですね。面白くもなんともなくて申し訳ないです。
配列を使う上での諸注意
「[ ]」のつける場所なんですが、これが慣れない内は非常に混乱します。よく見ると変数型の後ろにつけたり、配列変数名の後ろにつけたりしてますよね。
int[] scores; scores=new int[4]; scores[0] = 80;
これは覚えてしまいましょう。
・newする時は変数型の後ろ
・通し番号を入れる時は変数名の後ろ
です。あともう一つ、宣言する時です。宣言する時は実はどっちでもいいんです。
int[] scores; int scores[];
どちらでもOKなので、宣言については自分で分かりやすい・使いやすい方にしたらいいと思います。個人的には変数名に[ ]をつけるのは、どうも気持ち悪いので、型の方につけるようにしています。
ただし資格試験を受けるような方はこういうのを完全に覚える必要があります。このどっちでもいいとか、こっちじゃないとダメとかっていうのが非常にやっかいで、配列の宣言・初期化の問題は簡単そうに見えて案外間違います(体験談)。
参照型変数
さて、最後にもう一度、初期化のところを見てほしいのですが、
int[] scores; //①
scores = new int[4];//②
これは具体的に何をしているかと言うと、
①配列を入れるscoresという名前の箱を用意する。
②intを入れる箱を4つ用意して、それをscoresという箱の中に入れる(代入する)。
・・・これちょっと不思議じゃないですか?
まずscoresという箱を作ってから、その箱の中に後から別の箱を4つも持ってきて入れろと?4つならまだしも、もし100個も持って来られたら果たして入るのだろうか・・。そう考えると、初めに配列を格納する箱(scores)を用意する時、一体どんな大きさの箱を用意すればいいのか分かりませんよね。あくまで比喩ですが、一旦用意した箱を後から大きくしたりはできないですし、そもそも箱の中に他の箱を入れるなんてことはできません。けど、このソースコードを見る限り箱の中にを4つの箱をぶち込んでいるように見えますよね?一体どういうこと?
説明します。
scores = new int[4];
「new int[4]」で、int型変数(intを入れる箱)を4つ確かに作っているのですが、箱を作った後scoresに代入されるのは、4つの箱自体ではありません。
配列の要素(intを入れる箱4つ)は作った場所に置いたまま、その箱が置いてある場所を示す値が、配列変数scoresに代入されます。例えて言うなら、場所を示す番地が書かれた紙切れ一枚をscoresという箱に入れているようなものです。
それなら箱100個であろうとも、急に後から言われようとも代入できますよね。箱が何個であってもscoresに入れるのは紙切れ一枚で済むんですから(あくまで比喩です)。
このように変数(箱)の中に、値そのものではなく、「値を入れた箱の場所」を示す参照値(番地みたいなもの)が入っている変数を、参照型変数と言います。
配列変数は参照型変数の一種と言えます。それに対して、基本データ型変数は、用意した箱に値そのものが入っています。だから基本データ型変数は型の種類によってそれぞれ箱の大きさが決まっているんです(bit数)。ちなみに基本データ型変数にはその箱の大きさを超えるような値は入れられません(コンパイルエラーになります)。
さてここで問題です!
以下のプログラムで一体何が出力されるでしょうか?これは参照型変数と基本データ型変数の大きな違いが分かるプログラムです。ゆっくり一行ずつ理解していけば全然難しくはないので、何が出力されるか自分で考えてみてください。
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の値は何でしょうか??
分かりますでしょうか?本気で考えてみて下さい。参照型変数の仕組みを理解している人は、この問題が簡単に解けると思います。
答え合わせはご自身で実行してもらいたいところですが、それではさすがに不親切だと思うので、実行結果を載せておきます。
どうですか?正解していましたか?間違っていた人はこのプログラムがしていることを一行ずつ順を追って理解していってください。
ではもう一つ、おもしろいプログラムを。
Test2.java
public class Test2{
public static void main(String[] args){
int[] a = new int[]{1,2,3};
System.out.println(a);
}
}
配列変数aをprintlnメソッドの引数に渡すと、一体何が出力されるでしょうか?
「1,2,3」だと思った方、残念です。答えは紙切れに書いてある値です。しつこいようですが、配列変数aの中身は配列そのものではなく、ただの紙切れです。その紙切れに書かれているのは配列を置いてある場所を示す番地です。このプログラムを実行するとprintlnメソッドによってその「番地」が出力されます。「番地」とはどんなものなのか?暇な人はこのプログラムを(コピペして)実行してみて下さい。
check point
- 参照型変数に入っているのは参照先の番地
- 基本データ型変数に入っているのは値そのもの
配列の基本的な使い方、参照型変数というものの仕組みがちょっとは掴めましたでしょうか?(参照型についてのもうちょっと詳しい説明→基本データ型変数と参照型変数)
次回は繰り返し処理for文を使って、もうちょっと複雑なプログラムを作りたいと思います。
今だけ→転職できなければ全額返金の「エンジニア転職保証コース」
絶対エンジニアになる!→テックエキスパート
フリーランスエンジニアの収入例を見てみる→レバテックフリーランス
コメント
javaの勉強を始めたばかりで、とても解りやすく説明してあり感謝しています。
上記の表示があるのですが。
消して頂く事はできますか?
見づらくなりました。
by 匿名 2015/09/14 11:48
ご報告感謝いたします。ありがとうございました。
by Nobuo@管理人 2015/09/14 12:44
このどっちでもいいとか、こっちじゃないとダメどかっていうのが非常にやっかいで、配列の宣言・初期化の問題は簡単そうに見えて案外間違います(体験談)。→こっちじゃないとダメとかっていうのが非常にやっかいで、
by あさやん 2015/11/15 19:40
あさやんさん、ありがとうございます。
誤字脱字が多くて申し訳ないです。おかげで完成度が高まりました。^^
by Nobuo@管理人 2015/11/15 22:12
以下の二つの記述の違いがよくわからないです。
各要素の値を指定する初期化
・int[] scores = new int[]{80,65,70,95};
Test.javaの問1
・int[] a = new int[]{1,2,3};//①
前者は値を配列に代入していて
後者が箱を用意した場所を示した紙切れを代入している?
ということですが、同じように見えます。
by 初心者 2016/02/23 16:39
>前者は値を配列に代入していて後者が箱を用意した場所を示した紙切れを代入している?ということですが、同じように見えます。
そうです同じです。
前者も後者も同じく、配列変数に紙切れを代入しています。
by Nobuo@管理人 2016/02/23 17:50
管理人さんご回答ありがとうございます。
どちらもおなじ場所が書かれた紙切れということで理解しました。
問1において
a[0]!=1
a[1]!=2
a[2]!=3
a[3]は作成されていない
という認識になりました。
by 初心者 2016/02/24 08:59
すいません、質問させていただきます。
Test.javaの問2は理解できるのですが、問1だけがどうも理解できません。
問2は「cに1を代入」「dにcを代入」「dに5を代入」なので結果「c=1」となるのは理解できますが、問1も同じように考えると「a[0]=1」となってしまいます。
by 初心者2 2016/03/21 01:34
同じように考えてはダメです。こちらも参考にしてみてください。↓
【Java】 基本データ型 と 参照型 の違い
http://nobuo-create.net/sanshougata/
by Nobuo@管理人 2016/03/22 17:20
ここまで読み進めてきたのですが、問2が分かりません。
>aの参照値をbに代入するということは、aとbが同じ参照値を持つことになり、aもbも同じ実体(配列)を参照する状態になります。
これが分かりません。
bがaと同じ参照値を持つ(?)のは分かるのですが、なぜaがbと同じ参照値を持つ(?)のですか?
/*問1*/
int[] a = new int[]{1,2,3};//①
int[] b = a; //②
b[0] = 5; //③
System.out.println(a[0]);
②と③の間に
int[] a = b;
があれば答え5は理解できます。
int[] b = a;、かつ、int[] a = b;、なら、b[0] = 5;で、答え5は理解できます。
(初心者なのでがんばってこの気持ちを伝えたいのですが、なんていうえばいいのか分かりません。ごめんなさい)
なぜ、 int[] b = a;、だけで次の行で『bからも』(←これが分かりません)、[0]の参照値の中身(?)を書き換えることができるようになるのですか?
bからも参照値の中身を書き換えられるように何らかの処理をしなければならないのでは?
②と③の間に、int[] a = b;、を追加して、 int[] b = a;、かつ、int[] a = b;、にしなければ、bからは参照値の中身を書き換えられるようにならないのではありませんか?
元々、 int[] b = a;、と書けば、その意味するところは int[] b = a;、かつ、int[] a = b;、(初心者なので伝えたいことがうまく言葉にできません、分かって! この思い)、ということになるという仕様なのでしょうか。
「そうです、そういう仕様です!」というのなら、答え5は理解できます。
by 初心者3 2016/05/21 20:19