変数を用いるとデータを一つ格納できることは、すでに学びました。 たくさんのデータを格納するには、データの個数だけ変数を用意すれば可能です。 しかし、それらのデータを系統的に取り扱うことは困難です。
配列 ( array ) というものを用いると、たくさんのデータを系統的に格納することができます。 配列とは、データの「入れ物」が並んだものと考えてください。 次の図は、配列のイメージです。
「入れ物」の一つ一つを 配列要素 ( array element ) とよびます。 配列には次のような特徴があります。
int
型)の隣に実数(
double
型)を格納したりはできません。
変数を使うためには、変数の宣言が必要でした。
int x;
のように変数を宣言すると、変数
x
に整数(
int
型)が一つ格納できました。
配列を使うためには、配列の宣言と配列の生成という二段階の手続きが必要です。 次のプログラムは、配列を宣言し、生成し、その3番目に整数23を格納し、それを出力するものです。
/* 1*/ class ArrayTest { // 配列のテスト /* 2*/ public static void main (String[] args) { /* 3*/ int[] a; // 宣言 /* 4*/ a = new int[10]; // 10個生成 /* 5*/ a[3] = 23; // 3番目に格納 /* 6*/ System.out.println(a[3]); // 3番目を出力 /* 7*/ } /* 8*/ }
asiaa1:~/comp2b b08a001$ java ArrayTest 23 asiaa1:~/comp2b b08a001$
整数(
int
型)の場合、配列の宣言は次のように書きます。
int[] 配列;
ここで、「 配列 」は配列の名前です。 配列の生成は、
配列 = new int[要素数];
と書きます。 ここで、「 要素数 」は配列の要素数です。
上記のプログラムでは、3行目で名前が a である配列を宣言しています。 また、4行目で要素数が10である配列を生成しています。
配列要素にデータを格納するには、
配列[番号] = 式;
と書きます。
「
配列
」の「
番号
」番目の要素に「
式
」の値を格納します。
ここで、「
番号
」は数そのものである必要はありません。
数を値に持つならば、変数や式でもかまいません。
例えば、変数
i
の値が3で
x
の値が100ならば、
b[i - 1] = x + 1;
によって配列
b
の2番目の要素に101が格納されます。
式
配列[番号]
で、「 配列 」の「 番号 」番目の要素が取り出せます。 なお、配列の何番目の要素かを表すこの「 番号 」を、配列の 添字 ( subscript ) 、または インデックス ( index ) とよびます。
上記のプログラムでは、5行目で配列の3番目の要素にデータ23を格納しています。 また、6行目ではそのデータを取り出して出力しています。
上記のプログラムにおいて、 a [10] は存在しないことに注意してください。 このような、存在しない要素にデータを格納しようとしたり、データを取り出そうとすると、エラーが発生します。
/* 1*/ class ArrayError { // 配列のエラー /* 2*/ public static void main (String[] args) { /* 3*/ int[] a; /* 4*/ a = new int[10]; // 0番目から9番目まで /* 5*/ a[10] = 100; // 10番目は存在しない /* 6*/ System.out.println(100); /* 7*/ } /* 8*/ }
asiaa1:~/comp2b b08a001$ java ArrayError Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10 at ArrayError.main(ArrayError.java:5) asiaa1:~/comp2b b08a001$
なお、配列の要素数をどこかに記録しておく必要はありません。 これは、式
配列.length
によっていつでも取り出せます。
注意: 教科書によっては、配列の宣言は
int a[];
などと書くと説明しています。
Javaでは、
int[] a;
と
int a[];
は同じ意味です。
ただし、これらを混在させるのは混乱のもとですので、この授業では前者に統一します。
上記のイメージの通り、配列にデータを格納しましょう。 これは、配列の宣言、配列の生成、配列要素への代入という手順を踏むとできます。 以下はそのようなプログラム(部分)です。
int[] a; a = new int[10]; a[0] = 20; a[1] = 21; a[2] = 22; a[3] = 23; a[4] = 24; a[5] = 25; a[6] = 26; a[7] = 27; a[8] = 28; a[9] = 29;
次のように書くと、配列の宣言と生成が同時に行えます。
int[] a = new int[10]; // 宣言と生成
a[0] = 20; a[1] = 21; a[2] = 22; a[3] = 23; a[4] = 24;
a[5] = 25; a[6] = 26; a[7] = 27; a[8] = 28; a[9] = 29;
さらに、データの格納も同時に行うことができます。 これは次のように書きます。
int[] a = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
このことを、配列の
初期化
(
initialization
)
とよびます。
配列の初期化では、キーワード
new
や配列の要素数は書きません。
配列のプログラムでは、繰返しを利用するのが普通です。
つまり、ループ制御変数
i
を用意し、
for
文などの繰返しの中で、配列要素
a
[
i
]からデータを取り出したり、
a
[
i
]にデータを格納したりします。
次のプログラムは、配列 a の要素をすべて出力するものです。
/* 1*/ class ArrayPrint { // 配列の出力 /* 2*/ public static void main (String[] args) { /* 3*/ int i; /* 4*/ int[] a = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; // 初期化 /* 5*/ for (i = 0; i < a.length; i++) { // aの要素数回繰り返す /* 6*/ System.out.println("a[" + i + "] = " + a[i]); /* 7*/ } /* 8*/ } /* 9*/ }
asiaa1:~/comp2b b08a001$ java ArrayPrint a[0] = 20 a[1] = 21 a[2] = 22 a[3] = 23 a[4] = 24 a[5] = 25 a[6] = 26 a[7] = 27 a[8] = 28 a[9] = 29 asiaa1:~/comp2b b08a001$
4行目で配列
a
を初期化します。
式
a
.
length
の値は10なので、5行目の
for
文では変数
i
は0から9まで動きます。
6行目で配列要素
a
[
i
]の値を詳しく出力します。
配列の使用例として、はじめに絵グラフを出力します。 絵グラフとは、棒グラフの棒の代わりに、同じ絵が連続して描かれるグラフです。 ここでは、絵として星印(*)を用います。 グラフにするデータは以下の通りです。
添字 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
データ | 8 | 6 | 6 | 8 | 9 | 7 | 6 | 6 |
プログラムは次の通りです。 配列からデータを取り出すループと、星印を出力するループの、2重ループになっています。
/* 1*/ class PictureGraph { // 絵グラフ /* 2*/ public static void main (String[] args) { /* 3*/ int i, j; /* 4*/ int[] a = {8, 6, 6, 8, 9, 7, 6, 6}; /* 5*/ for (i = 0; i < a.length; i++) { /* 6*/ System.out.print(i + ": "); /* 7*/ for (j = 0; j < a[i]; j++) { // a[i]回繰り返す /* 8*/ System.out.print("*"); /* 9*/ } /* 10*/ System.out.println(); /* 11*/ } /* 12*/ } /* 13*/ }
asiaa1:~/comp2b b08a001$ java PictureGraph 0: ******** 1: ****** 2: ****** 3: ******** 4: ********* 5: ******* 6: ****** 7: ****** asiaa1:~/comp2b b08a001$
次の例は、配列要素の総和を求めるものです。
{100, 200, 100, 300, 200}
で表される配列の要素の合計を、レジの計算の要領で算出します。 つまり、
sum += a[0]; sum += a[1]; sum += a[2]; ...
のように計算します。 プログラムは次の通りです。
/* 1*/ class ArraySummation { // 配列の総和 /* 2*/ public static void main (String[] args) { /* 3*/ int i, sum = 0; /* 4*/ int[] a = {100, 200, 100, 300, 200}; /* 5*/ for (i = 0; i < a.length; i++) { /* 6*/ sum += a[i]; /* 7*/ } /* 8*/ System.out.println("合計は" + sum); /* 9*/ } /* 10*/ }
asiaa1:~/comp2b b08a001$ java ArraySummation 合計は900 asiaa1:~/comp2b b08a001$
次の例は、配列要素の最大値を求めるものです。
{100, 200, 100, 300, 200}
で表される配列の要素の最大値は、人間が見れば 300 だと分かります。
この問題をコンピュータで解決するのに、勝ち抜き方式を使うことにします。 これは、はじめに最初の要素をとりあえずチャンピオンにします。 続いて、次の要素とチャンピオンが勝負をし、チャンピオンが負けたら勝者を新たにチャンピオンにします。 これを要素がなくなるまで繰り返します。 最後のチャンピオンが全体のチャンピオンであるというわけです。
具体的には、
max = a[0]; if (a[1] > max) { max = a[1]; } if (a[2] > max) { max = a[2]; } ...
のようにして、最大値を求めます。 プログラムは次の通りです。
/* 1*/ class ArrayMaximum { // 配列の最大値 /* 2*/ public static void main (String[] args) { /* 3*/ int i, max; /* 4*/ int[] a = {100, 200, 100, 300, 200}; /* 5*/ max = a[0]; // とりあえずチャンピオンにする /* 6*/ for (i = 1; i < a.length; i++) { /* 7*/ if (a[i] > max) { // チャンピオンが負けたら /* 8*/ max = a[i]; // 勝者をチャンピオンにする /* 9*/ } /* 10*/ } /* 11*/ System.out.println("最大値は" + max); /* 12*/ } /* 13*/ }
asiaa1:~/comp2b b08a001$ java ArrayMaximum 最大値は300 asiaa1:~/comp2b b08a001$
最後の例として、成績の集計を行います。 30人のクラスで試験をして、次のような得点データが得られたとします。
95, 80, 65, 0, 85, 100, 79, 85, 93, 0, 88, 90, 80, 98, 75, 95, 58, 85, 88, 70, 75, 82, 100, 60, 92, 55, 0, 95, 83, 81
これを10点刻みで分類し、得点分布を求めるプログラムを考えます。
はじめに、人数を数える配列 counts を用意します。 得点とこの配列の添え字を次のように対応させます。
得点 | 添字 |
---|---|
0〜9 | 0 |
10〜19 | 1 |
20〜29 | 2 |
30〜39 | 3 |
40〜49 | 4 |
50〜59 | 5 |
60〜69 | 6 |
70〜79 | 7 |
80〜89 | 8 |
90〜99 | 9 |
100 | 10 |
このように対応させると、得点 s から添字を求めるには、式 s /10 で計算できます。 また、添字 i から得点範囲を求めるには、式 i ×10 と i ×10+9 で計算できます。
次に、得点を格納する配列 scores を用意します。 配列の初期化を使って、これに得点を格納します。 このデータをひとつずつ取り出し、得点から添字を計算し、その分類の人数を1増加させるという操作を繰り返して、得点範囲ごとに人数を数えます。
10行目から12行目で、各分類の人数 counts [ i ] をすべて0にします。 13行目から15行目で、各得点 scores [ i ] から添字 scores [ i ] / 10 を求め、その分類の人数を1増やします。
最後に集計結果を出力します。 17行目で得点範囲を、18行目でその人数を、それぞれ改行せずに出力します。 改行は19行目で行います。
/* 1*/ class ScoreDistribution { // 得点の分布 /* 2*/ public static void main (String[] args) { /* 3*/ int i; /* 4*/ int[] counts = new int[11]; // 人数を格納する配列 /* 5*/ int[] scores = { /* 6*/ 95, 80, 65, 0, 85, 100, 79, 85, 93, 0, /* 7*/ 88, 90, 80, 98, 75, 95, 58, 85, 88, 70, /* 8*/ 75, 82, 100, 60, 92, 55, 0, 95, 83, 81 /* 9*/ }; /* 10*/ for (i = 0; i < counts.length; i++) { /* 11*/ counts[i] = 0; // 最初は0人 /* 12*/ } /* 13*/ for (i = 0; i < scores.length; i++) { /* 14*/ counts[scores[i] / 10]++; // 人数を1増加 /* 15*/ } /* 16*/ for (i = 0; i < counts.length; i++) { /* 17*/ System.out.print((i * 10) + " - " + (i * 10 + 9) + " : "); /* 18*/ System.out.print(counts[i]); // 人数を出力 /* 19*/ System.out.println(); /* 20*/ } /* 21*/ } /* 22*/ }
asiaa1:~/comp2b b08a001$ java ScoreDistribution 0 - 9 : 3 10 - 19 : 0 20 - 29 : 0 30 - 39 : 0 40 - 49 : 0 50 - 59 : 2 60 - 69 : 2 70 - 79 : 4 80 - 89 : 10 90 - 99 : 7 100 - 109 : 2 asiaa1:~/comp2b b08a001$
ある会社のある課には社員が30人います。 社員の年齢の分布を調べることになりました。 10代、20代、30代、40代、50代、60代の6種類に分類し、以下のように出力するプログラムを作成してください。 年齢のデータは次の通りです。
62, 48, 30, 55, 23, 47, 58, 33, 34, 57, 36, 19, 51, 41, 63, 28, 42, 32, 55, 64, 52, 60, 25, 63, 44, 30, 51, 37, 39, 44
このデータをプログラムの中に書き、配列の初期化の要領で配列に格納してください。 なお、10才未満や70才以上の人は、はじめからいないものとしてください。
asiaa1:~/comp2b b08a001$ java AgeDistribution 10代: 1 20代: 3 30代: 8 40代: 6 50代: 7 60代: 5 asiaa1:~/comp2b b08a001$
余力のある人は、絵グラフを出力してください。
asiaa1:~/comp2b b08a001$ java AgeDistribution2 10代: * 20代: *** 30代: ******** 40代: ****** 50代: ******* 60代: ***** asiaa1:~/comp2b b08a001$
今日の演習9の答案(Javaプログラム)をメールで提出してください。 差出人は学内のメール・アドレス(b08a001@cis.twcu.ac.jpなど)とし、宛先はkonishi@cis.twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(6月24日)を明記してください。