第 2 章 Java 言語の基本的な文法 I 5
2.14 配列
2.14.3 配列と繰り返し
配列は、繰り返しと実に相性の良い構造です。要素を番号で管理できるので、配列内の値を番号順に処理することを 繰り返し命令で指示できます。まず、配列に入っている値をコンソールに順番に出力してみましょう。
ソースコード2.42 PrintArray.java (配列と繰り返し) package section0214;
public class PrintArray {
public static void main(String[] args) { int[] degree = { 90, 85, 72, 100, 95 };
for(int i=0; i<degree.length; i++) { System.out.println( degree[i] );
} } }
for文の2項目のdegree.length、この説明を先にしておきましょう。配列を定義すると、「配列名.length」で、そ の配列の要素数を得ることができます。この場合、degree.lengthは5ですね。要素はdegree[0]からdegree[4]
に入っていますが、要素数はあくまで5 です。実際に入っている番号を考えると、for(int i=0; i<=4; i++)と直 に書きたいところですが、ここはdegree.lengthを使います。なぜかというと、データはプログラムが完成した後も 変更があるかもしれないからです。もし配列の要素が増減したら、for文も変更しなければなりません。プログラムの 一部を変更したとき、他の場所も変更しなければならなくなるようでは、汎用性の低いプログラムです。配列の要素数 が変わってもdegree.lengthを使っておけば、for文の変更は不必要になる、それが良いプログラムというわけです。
と言うことで、for(int i=0; i<=degree.length-1; i++)と書くのかな?と思うと、こうした場合Javaを使う 人たちの慣例で、等号を使わずに、for(int i=0; i<degree.length; i++)と書くんですね。iがint型ですから、
これでも大丈夫。ちょっと慣れが必要ですかね。
問題 2.14.3. PrintArray.javaを次のように改良しなさい。
1. 1つずつの値の出力を改行せずに、値と値の間に1 空白ずつ挟んで(90 85 72 100 95)、全体で 1行に出力 するプログラムPrintArray2.javaを作りなさい。最初のカッコと最後のカッコを繰り返しの前後に別に書きま しょう。
2. PrintArray2.javaの出力順を逆順にしたプログラムPrintArray3.javaを作りなさい。つまり、要素を後ろから
順に出力する(95 100 72 85 90)。
3. 実は、配列の中身を出力したいときにこうして自分で繰り返し処理を書かなくても済む方法がJavaには用意さ れています。 配列名degreeに対し、System.out.println( Arrays.toString( degree ) );という命令 で配列の中身を出力することができます。 ただし、import java.util.Arrays; とインポート文を加えない といけません。PrintArray4.javaとして作りなさい。以降は、単純な配列の出力は繰り返しで頑張って書く のはやめて、これで行きましょう。
配列の初期値を繰り返しで作ってみましょう。長さ 10の整数配列を作り、そこに初期値 { 1, 2, 3, ..., 10 } を入れるのを繰り返し文で行っています。カウンタのiに1加えた値を代入する、というところがミソですね。
ソースコード2.43 ArrayInitialization2.java (繰り返しによる配列の初期化) package section0214;
import java.util.Arrays;
public class ArrayInitialization2 {
public static void main(String[] args) { int[] array = new int[10];
for(int i=0; i<array.length; i++) { array[i] = i + 1;
}
System.out.println( "array = " + Arrays.toString( array ) );
} }
問題 2.14.4. 次のような長さ10の初期ベクトルを作る繰り返し処理を書きなさい。
1. a={2,4,6,8,10,· · ·,20} 2. a={1,2,4,8,16,32,· · ·,512} 3. a={1,−1,1,−1,1,−1,· · · ,−1}
次は、配列内の要素の合計値を求めてみましょう。
合計値を計算するには、配列とは別にその値を代入する変数が必要です。以下のプログラムではsumがその変数です が、配列の要素がすべて int型なのですから、その合計値もint型となりますね。そこで、int sum = 0;が必要と なります。配列と違って、基本型の変数はデフォルト値が自動で代入されはしません。つまり、int sum;だけではそ の後の sum += degree[i];ができないということです。ちゃんと初期値を代入します(int sum = 0;)。
ところで、int sum = 0;この命令をプログラムの何処に置いたら良いか?ちゃんと理解して下さいね。結構、この 置き場所がわからずに前期試験で敗退!の人が多いのです。よく間違えるのが、for(int i=0; i<degree.length;
i++) { の次に書く人です。それでは、繰り返しが行われるたびに、毎回sumに 0を代入されることになり、せっか
く1つずつ要素を加えていっても、次の繰り返しで 0クリアされてしまう。つまり、合計が計算できません。それに、
for(int i=0; i<degree.length; i++) {の次に書いてしまったら、変数sumのスコープはどこまででしょう?そ う、繰り返しの中だけですよね。つまり、繰り返し後にその値を出力したくても、メモリから消えてしまいます。
ソースコード2.44 SumOfArray.java (配列の要素の合計) package section0214;
public class SumOfArray {
public static void main(String[] args) { int[] degree = { 90, 85, 72, 100, 95 };
int sum = 0;
for(int i=0; i<degree.length; i++) {
// ここに int sum = 0; を入れて×になる人が多い sum += degree[i];
}
System.out.println( "要素の合計は、" + sum );
} }
ということで、小さなプログラムですが、しっかり理解して下さい。
続いて、配列内の要素の最大値を求めてみましょう。こちらも最大値を記憶するための変数 maxの初期化int max
= 0; の場所を間違わないで下さい。
ソースコード2.45 MaxOfArray.java (配列の要素の最大値) package section0214;
public class MaxOfArray {
public static void main(String[] args) { int[] degree = { 90, 85, 72, 100, 95 };
int max = 0;
for(int i=0; i<degree.length; i++) { if( degree[i] > max ) {
max = degree[i];
} }
System.out.println( "最大値は、" + max );
} }
少々難しくなってきましたが、まずは解読してみて下さい。これで最大値が変数maxに取り出されて出力される、と いうことを理解できましたか。定期試験では、こうした基本的なプログラムを解読する能力を問いますので。
さて、このプログラム1 箇所問題点があります。何処でしょう?
最大値を代入するための変数maxに初期値として0 を代入していますが、もし、配列の要素が全て負の数だったらど うなりますか?まずいですね。ではmaxには最初どんな値を代入すれば良いでしょう。方法は、2 通り考えられます。
1. 整数の最小値を代入する方法:Java でも無限大や無限小の値は表せません。そこで、代わりに int型の 最小値 Integer.MIN_VALUE を使います。(ソースコード 2.5 IntegerMaxAndMin.java 参照)。そうすれば、
degree内の値がどんなに小さな負の数でも、最小値より小さいことは無いので、degree[0]との比較で、必ず
max = degree[0];が行なわれます。
2. degree[0]を代入する方法:1. の方法だと必ず繰り返しの初回に max = degree[0];が行なわれます。なら ば、繰り返しの前にmax = degree[0];を行ってしまおう、という方法です。そうしておいて、繰り返しは int i=1から始めれば良いわけです。
なお、こちらの方法は1 つ弱点があって、もし配列の長さが0 もしくは未定義だったとき、エラーが発生しま す(2.14.6 節 参照)。
プログラムを作るとき、想定しているデータがどんな範囲なのかを良く検討し、想定外だったので暴走!なんてこと がないようにせねばいけません。このdegreeが試験の成績なら負の数は起こりませんが、気温とか実験データとかで 負の数もあり得るようなら、上のプログラムは問題ですね。
問題 2.14.5. 同じ整数配列 degreeを用いて、配列内の値の最小値を求めるプログラム MinOfArray.java を作りな さい。
問題 2.14.6. 同じ整数配列 degreeを用いて、配列内の値の平均値を求めるプログラムAverageOfArray.javaを作り なさい。ただし、平均値は実数になりますので、ご注意を!
配列自体は整数値の配列ですが、平均値は実数値ですね。単純に合計値を個数で割り算すると「整数÷整数」が起き て小数部の情報が消えてしまいます。それはまずいので、実数へのキャストを利用しなければいけません。
この辺のデータ集計プログラムは基本処理として重要なので、しっかり自分のものにして下さい。
問題 2.14.7. 整数配列xに対して、配列内の正の値のみの平均値を求めるプログラム AverageOfPositive.javaを作 りなさい。配列の内容が変わっても破綻しないように作ること。なお、配列には必ず正の値が入っているものとしま す。入っていないと平均値は求められないからね。
ソースコード2.46 AverageOfPositive.java (配列内の正数の平均値) package section0214;
public class AverageOfPositive {
public static void main(String[] args) {
int[] x = { 15, -30, -18, 0, 25, 32, -5, 16, 51 };
// 繰り返しを用いて、x の値を1つずつ正か否かのチェックをし 、 // 正の数の和と個数をカウント 、繰り返しが終わったら 、平均値を求める // 和のための変数と個数のための変数 、それを何処に置くか 、誰にも聞かずに // 独力で書けますか?
} }
問題 2.14.8. オリンピックの採点方式(与えられた実数配列に入っている点数の内で、最高点と最低点を1つずつ除
いた残りの点数の平均点)を計算・出力するプログラムOlympicGame.javaを作りなさい。なお、配列の長さは必ら ず3 以上であるとします。
ソースコード2.47 OlympicGame.java (オリンピック採点方式) package section0214;
public class OlympicGame {
public static void main(String[] args) {
double[] points = {9.95, 9.82, 9.95, 10.0, 9.64, 9.55, 10.0};
// 配列 points に入っている点数の合計・最高点・最低点を求めて // 合計から最高点&最低点を引いて 、points の長さより 2 小さい // 数で割ることで平均点が求まり 、それをコンソールに出力する // 例えば、"成績は、9.872 でした" なんて感じに
} }
次は、長さの等しい2 つのベクトルの差ベクトルを求めてみましょう。結果の値を入れるベクトルを同じ長さで宣 言しておいて、そこへ要素を1つずつ代入していきます。
ソースコード2.48 DifferenceTwoArray.java (ベクトルの差) package section0214;
import java.util.Arrays;
public class DifferenceTwoArray {
public static void main(String[] args) { int[] x = { 90, 85, 72, 100, 95 };
int[] y = { 82, 70, 98, 68, 70 };
int[] z = new int[x.length];
for(int i=0; i<z.length; i++) { z[i] = x[i] - y[i];
}
System.out.println( "x - y = " + Arrays.toString( z ) );
} }
配列を扱うプログラムは、このように要素を 1 つずつ繰り返しの中で計算する場合が多いので、よく理解して下 さい。
問題 2.14.9. 長さの等しい 2 つのベクトルに入っている値をすべて交換するプログラムSwapArray.java を作りな さい。
ソースコード2.49 SwapArray.java (2つのベクトルの交換) package section0214;
public class SwapArray {
public static void main(String[] args) { int[] a = {13, 53, 24, 67, 83, 40};
int[] b = {20, 14, 61, 94, 92, 50};
// この2つのベクトルの対応する要素同士を交換する // a[0]⇔b[0], ..., a[a.length-1]⇔b[b.length-1]
// 交換が終わったら、以下でそれぞれを出力してみる
System.out.println("a = " + Arrays.toString( a ) );
System.out.println("b = " + Arrays.toString( b ) );
} }
問題 2.14.10. 整数配列 aに入っている中身を、他の配列を使わずに逆順にする、つまりa[0]とa[a.length−1]を、
a[1]とa[a.length−2]を、… と交換するプログラムToReverseOrder.javaを作りなさい。
ソースコード2.50 ToReverseOrder.java (配列の要素の逆順化) package section0214;
public class ToReverseOrder {
public static void main(String[] args) {
int[] a = { 11, 19, 15, 17, 21, 13, 23, 27, 25 };
// この後、配列の値を出力し 、 // 続いて、要素を逆順に 。
// そして、再度 配列の値を出力する }
} 出力例 :
11 13 15 17 19 21 23 25 27 27 25 23 21 19 17 15 13 11
問題2.14.11. int型配列degreeに学生の成績が入っているので、評価( S, A, B, C, D )毎の人数を集計したい。評 価毎の人数をint型配列 gradeに集計し、以下のように出力するプログラムEvaluateGrade.javaを作りなさい。
ソースコード2.51 EvaluateGrade.java (成績評価の集計) package section0214;
public class EvaluateGrade {
public static void main(String[] args) {
int[] degree = {82, 90, 65, 30, 80, 66, 72, 59, 20, 90, 75, 84, 68, 75, 45, 76, 90, 98, 55, 42, 100, 92, 78, 85, 72, 45, 62, 70, 82, 95,
60, 38, 92, 85, 78, 55, 80, 74, 92, 83 };
// grade[0]:評価 S の人数を入れる ... grade[4]:評価 D の人数を入れる int[] grade = { 0, 0, 0, 0, 0 };
String[] gradeName = { "S", "A", "B", "C", "D" };
// ここに、degree の値を1つずつチェックし 、どの評価になるか調べ 、
// grade の対応する要素に1加える処理を人数分繰り返す処理を書く
// 例えば、点数が90点以上なら S だから、grade[0] に1加える。となる // 出来上がった評価の配列を以下で出力する
for(int i=0; i<grade.length; i++ ) {
System.out.println( "評価 " + gradeName[i] + " の人数は、" + grade[i]
+ "人です" );
} } }