基本型の一次配列

みるくあいらんどっ! > ドキュメント > Java > じっくり学ぶ Java講座 [初心者向け・入門]


配列の基礎

配列を使用しないプログラム

あらかじめ設定した 5人のスコアの合計を算出するプログラムを製作することにします。仕様を以下に定めます。

  • 5人のスコアはあらかじめ設定する(変数を 5つ用意する)。
  • 5人のスコアはそれぞれ「70, 50, 80, 40, 90」とする。

この仕様を満たすソースコードは以下の通り。

J101/J101.java

/**
 * 5人のスコアの合計を求めます(配列を使用しない)。
 */
public class J101 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		int score1 = 70;
		int score2 = 50;
		int score3 = 80;
		int score4 = 40;
		int score5 = 90;
		
		// 合計スコア
		int sum;
		
		sum = score1 + score2 + score3 + score4 + score5;
		
		// 表示
		System.out.println("1人目のスコアは " + score1);
		System.out.println("2人目のスコアは " + score2);
		System.out.println("3人目のスコアは " + score3);
		System.out.println("4人目のスコアは " + score4);
		System.out.println("5人目のスコアは " + score5);
		
		System.out.println("スコアの合計は " + sum);
	}
}

実行結果は以下の通り。

1人目のスコアは 70
2人目のスコアは 50
3人目のスコアは 80
4人目のスコアは 40
5人目のスコアは 90
スコアの合計は 330

これまでの復習なので大丈夫だと思いますが、念のため以下に図を記述しますね。なお、String型インスタンスについては、図の中に含めると煩わしいため、省略することにします。

配列を使用しない場合

スコアを格納するための 5つの変数 score1, score2, score3, score4, score5と、合計を格納するための変数 sumを使用しています。これによって、5人のスコアを計算することはできました。

でも、もし 5人ではなくて 100人になったらどうだろう? 数値を 100個並べるところ(11~15行目)は仕方が無いと諦めたしても、合計スコアを計算するところ(20行目)と、スコアの表示のところ(23~27行目)を書き換えるのはとても大変です。

このように、同じ型(今回は int型)の変数をたくさん用意していろいろな処理をしたい場合、配列を利用すると、シンプルに処理を書けることがあります。配列と繰り返し(for文など)を組み合わせた例を見ていきます。

配列を使用したプログラム

先と同じプログラムを、配列を使用して実現してみます。

ソースコードは以下の通り。

J102/J102.java

/**
 * 5人のスコアの合計を求めます(配列を使用したが、繰り返しを使用していない)。
 */
public class J102 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		// int[]型の参照型変数の宣言
		int[] scores;
		
		// 配列インスタンスの生成と参照型変数への紐づけ
		scores = new int[5];
		
		// 配列インスタンスの各要素への値の代入
		scores[0] = 70;
		scores[1] = 50;
		scores[2] = 80;
		scores[3] = 40;
		scores[4] = 90;
		
		// 合計スコア
		int sum;
		
		sum = scores[0] + scores[1] + scores[2] + scores[3] + scores[4];
		
		// 表示
		System.out.println("1人目のスコアは " + scores[0]);
		System.out.println("2人目のスコアは " + scores[1]);
		System.out.println("3人目のスコアは " + scores[2]);
		System.out.println("4人目のスコアは " + scores[3]);
		System.out.println("5人目のスコアは " + scores[4]);
		
		System.out.println("スコアの合計は " + sum);
	}
}

実行結果は以下の通り。

1人目のスコアは 70
2人目のスコアは 50
3人目のスコアは 80
4人目のスコアは 40
5人目のスコアは 90
スコアの合計は 330

12行目にて int[]型の参照型変数 scoresを宣言しています。続いて 15行目にて、大きさが 5の int[]型の配列インスタンスを、予約語newを用いて作成しています。作成したインスタンスを参照型変数 scoresに紐づけることによって、その後、scores[0], scores[1], scores[2], scores[3], scores[4]を、変数のように利用できるようになりました。配列の添え字は 1からのスタートではなく 0からのスタートである点に注意してください。

イメージとしては以下のような感じです。

配列を使用した場合

配列インスタンス内の各要素はインスタンスフィールドなので、初期値は 0で、18~22行目の代入によって値が設定されます。

注意しなければならないのは、変数 scoreは参照型だという点。scores[0]とは、「参照型(int[]型)の変数 scoresが指し示す先にある配列インスタンスの 0番目の int型変数を表している」ということです。scoresはあくまでも参照型変数、配列インスタンスは名前のないインスタンス、そして、両者が結びつくことで、事実上、名前が与えられているということになります。

配列を利用したものの、繰り返し文は使用していません。これだけだと、ただプログラムが複雑になっただけですね。スコアを計算する人数が 5人から 100人に変わったとしたら、合計スコアを計算するところも、スコアの表示のところも書き換えるのはとても大変です。

続いて、配列と繰り返し文を組み合わせた例を見ていきます。

配列と繰り返しを使用したプログラム

ソースコードは以下の通り。

J103/J103.java

/**
 * 5人のスコアの合計を求めます(配列と繰り返しを使用した)。
 */
public class J103 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		// int[]型の参照型変数の宣言
		int[] scores;
		
		// 配列インスタンスの生成と参照型変数への紐づけ
		scores = new int[5];
		
		// 配列インスタンスの各要素への値の代入
		scores[0] = 70;
		scores[1] = 50;
		scores[2] = 80;
		scores[3] = 40;
		scores[4] = 90;
		
		// 合計スコアを宣言し、0で初期化する
		int sum = 0;
		
		for(int i = 0; i < scores.length; i ++) {
			sum += scores[i];
		}
		
		// 表示
		for(int i = 0; i < scores.length; i ++) {
			System.out.println((i + 1) + "人目のスコアは " + scores[i]);
		}
		
		System.out.println("スコアの合計は " + sum);
	}
}

実行結果は以下の通り。

1人目のスコアは 70
2人目のスコアは 50
3人目のスコアは 80
4人目のスコアは 40
5人目のスコアは 90
スコアの合計は 330

イメージとしては以下のような感じです。

配列と繰り返しを使用した場合

まず、2ヶ所で使用されているscores.lengthについて。配列インスタンスはインスタンスフィールドlengthを持っています。このインスタンスフィールドには、配列の大きさが格納されています。したがって、今回のプログラムの場合は「5」が格納されていることになります。インスタンスフィールド lengthを使用することで、じかに「5」と記述することを避けています。今後、スコアを計算する人数が 5人から 100人に変わったときなどに、ソースコードの変更箇所を減らすことができます。

繰り返し文を用いて、配列の要素にアクセスしていることが理解できるでしょうか。合計の計算は、sumにそれぞれの値を加算していくことで実現しています。また、各スコアの表示についても繰り返し文で実現しています。今後、スコアを計算する人数が 5人から 100人に変わったとしても、24行目以降はまったく変更する必要がありません。

配列インスタンスの初期化

配列インスタンスの初期化を利用した例を挙げてみます。

ソースコードは以下の通り。

J104/J104.java

/**
 * 5人のスコアの合計を求めます。
 */
public class J104 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		// int[]型の参照型変数の宣言
		int[] scores;
		
		// 配列インスタンスの生成と初期化、および参照型変数への紐づけ
		scores = new int[] {70, 50, 80, 40, 90};
		
		// 合計スコアを宣言し、0で初期化する
		int sum = 0;
		
		for(int i = 0; i < scores.length; i ++) {
			sum += scores[i];
		}
		
		// 表示
		for(int i = 0; i < scores.length; i ++) {
			System.out.println((i + 1) + "人目のスコアは " + scores[i]);
		}
		
		System.out.println("スコアの合計は " + sum);
	}
}

実行結果は以下の通り。

1人目のスコアは 70
2人目のスコアは 50
3人目のスコアは 80
4人目のスコアは 40
5人目のスコアは 90
スコアの合計は 330

15行目にて、配列のインスタンスの生成と同時に初期化を行っています。配列の大きさ「5」は指定する必要がないことに注意してください。配列の大きさは、初期値の数に応じて自動的に設定されます。

イメージとしては以下のような感じです。

配列の初期化

先のイメージと少しだけ違うのが分かるでしょうか。

配列の参照型の宣言と配列インスタンスの初期化

配列の参照型の宣言と配列インスタンスの生成・初期化を一括で行う場合、さらに簡易的に記述できます。

ソースコードは以下の通り。

J105/J105.java

/**
 * 5人のスコアの合計を求めます。
 */
public class J105 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		// int[]型の参照型変数の宣言
		// 配列インスタンスの生成と初期化
		// 配列インスタンスの参照型変数への紐づけ
		int[] scores = {70, 50, 80, 40, 90};
		
		// 合計スコアを宣言し、0で初期化する
		int sum = 0;
		
		for(int i = 0; i < scores.length; i ++) {
			sum += scores[i];
		}
		
		// 表示
		for(int i = 0; i < scores.length; i ++) {
			System.out.println((i + 1) + "人目のスコアは " + scores[i]);
		}
		
		System.out.println("スコアの合計は " + sum);
	}
}

実行結果は以下の通り。

1人目のスコアは 70
2人目のスコアは 50
3人目のスコアは 80
4人目のスコアは 40
5人目のスコアは 90
スコアの合計は 330

これは「new int[]が省略されている」とみなすこともできます。予約語newが使用されていない場合においても、配列インスタンスが生成されていることに変わりないことに注意してください。

イメージとしては以下のような感じです。

配列の初期化

配列はインスタンス

この項は「基本型の一次配列」と銘打っていますが、配列は常にインスタンスです。配列は「基本型を扱う配列」と「参照型を扱う配列」に分類されますが、配列そのものは常にインスタンスであることに注意してください。

配列はインスタンスですので、他のインスタンス同様に名前がありません。参照型(今回の場合は int[]型)の変数を紐づけることによって、事実上、名前が与えられることになります。

配列の大きさは変えられない

配列インスタンスの大きさは、配列インスタンスの生成時に指定しなければなりません。そして、この大きさを後で変更することはできません。

配列を使用したプログラム

複数人のスコアを合計・平均する

複数人のスコアを合計し平均を求めるプログラムを製作します。仕様を以下に定めます。

  • ユーザから価格の人数の入力を受け付ける。
  • 人数の回数だけ、スコアの入力を受け付ける。
  • 各スコアと、合計スコア、スコアの平均を表示する。

この仕様を満たすソースコードを以下のように製作しました。

J106/MySystem.java(ライブラリをそのまま利用します)

J106/J106.java

/**
 * 複数人のスコアの合計と平均を表示します。
 */
public class J106 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		int[] scores;
		int number;
		
		number = MySystem.in.getInt("人数を入力してください");
		
		if(number <= 0) {
			System.out.println("人数が 0以下です。");
		} else {
			scores = new int[number];
			
			// スコアの入力
			for(int i = 0; i < scores.length; i ++) {
				scores[i] = MySystem.in.getInt((i + 1) + "人目のスコアは");
			}
			
			// 合計の計算
			int sum = 0;
			for(int i = 0; i < scores.length; i ++) {
				sum += scores[i];
			}
			
			// 平均の計算
			double average = (double)sum / scores.length;
			
			// 表示
			System.out.println("---");
			for(int i = 0; i < scores.length; i ++) {
				System.out.println(( i + 1) + "人目のスコアは " + scores[i]);
			}
			System.out.println("合計は " + sum);
			System.out.println("平均は " + average);
		}
		
	}
}

実行結果の例は以下の通り。

人数を入力してください? 3
1人目のスコアは? 100
2人目のスコアは? 50
3人目のスコアは? 70
---
1人目のスコアは 100
2人目のスコアは 50
3人目のスコアは 70
合計は 220
平均は 73.33333333333333

月の末日を表示する

月の末日を表示するプログラムを製作します。仕様を以下に定めます。

  • ユーザから年と月の入力を受け付ける。
  • その年と月の場合の末日を表示する。例えば、1996年4月の場合、「30日」と表示する。
  • うるう年を正しく処理する。うるう年の判定処理は、以前製作したメソッド(G105.java)から再利用する。

この仕様を満たすソースコードを以下のように製作しました。

J107/MySystem.java(ライブラリをそのまま利用します)

J107/J107.java

/**
 * 月の末日を表示します。
 */
public class J107 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		int year;
		int month;
		int[] days;
		
		year = MySystem.in.getInt("西暦を入力してください");
		
		if(year < 0) {
			System.out.println("年の値が不正です。");
		} else {
			month = MySystem.in.getInt("月を入力してください");
			
			if(month < 1 || month > 12) {
				System.out.println("月の値が不正です。");
			} else {
				
				// うるう年かどうかで利用する配列を切り替える
				if(J107.isLeapYear(year)) {
					days = new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
				} else {
					days = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
				}
				
				System.out.println(year + "年 " + month + "月の末日は " + days[month - 1] + "日です。");
			}
		}
	}
	
	/**
	 * 指定した年がうるう年かどうかを判定します。
	 * 年がマイナスでも計算してしまいます、注意。
	 * @param year 年
	 * @return うるう年の場合、true
	 */
	private static boolean isLeapYear(int year) {
		if(year % 400 == 0) {
			return true;
		} else if(year % 100 == 0) {
			return false;
		} else if(year % 4 == 0) {
			return true;
		} else {
			return false;
		}
	}
}

実行結果の例は以下の通り。

西暦を入力してください? 2013
月を入力してください? 1
2013年 1月の末日は 31日です。
西暦を入力してください? 2004
月を入力してください? 2
2004年 2月の末日は 29日です。
西暦を入力してください? 1900
月を入力してください? 2
1900年 2月の末日は 28日です。

配列の要素外へのアクセス

配列の要素外へアクセスするとどうなるでしょうか。

ソースコードは以下の通り。

J108/J108.java

/**
 * 配列の要素外にアクセスする。
 */
public class J108 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		int[] scores = { 70, 50, 80, 40, 90};
		
		System.out.println("10人目のスコアは " + scores[9]);
	}
}

実行結果は以下の通り。

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 9
	at J108.main(J108.java:13)

今回利用した配列は大きさが 5なので、添え字は 0~4の範囲でなければなりません。それ以外の値を指定すると、例外ArrayIndexOutOfBoundsExceptionが発生します。この例外は、配列を扱ったプログラムを製作する際には、しばしば遭遇することになると思います。例外についての詳細は、後の章にて解説します。

最終更新: 2013/02/10 , 公開: 2013/02/09
▲top