配列

配列は同じデータ型の要素データを指定個数集めて一つの配列変数として扱えるようにしたものです。
複数の要素データを一つのまとまりとして扱う方法は配列以外にもいくつかありますが、配列の特徴

1次元だけでなく、2次元以上の多次元でも要素を構造化し、int型の整数で要素を特定することが出来る。
配列を宣言し、その領域を割り当てると配列の再構成はできない。(要素の追加・削除はできない)。

配列の作成は3段階に分かれていますが、記述の仕方で一度にすべて行うことも出来ますが、基本は次の順で行わなければいけません。

1.
配列の宣言
2.
配列の領域割当て(配列インスタンスの作成)
3.
配列要素の設定(値の代入)

配列の宣言

書式
要素データの型 次元を表す[ ]のセット 配列要素名;

ですが、Javaの変数宣言に類似した語順である形式1とC言語などの変数宣言に類似した形式2の2種類の表記法があります
形式1表記 int[] array;
形式2表記 int array[];

多次元配列は[ ]の数で次元を指定します。
多次元配列の宣言でも形式1表記、形式2表記が選択できます。

int[ ][ ] arrayA;    2次元配列型変数の宣言
int arrayB[ ][ ][ ];  3次元配列型変数の宣言

なお形式1表記、形式2表記を併用しても構いません。つまり、[ ]を一度にまとめて記述する必要はありません

int[] arrayA[];
int[][] arrayB[];

配列の宣言時には値がnullの配列型変数を確保するだけで、配列用の領域確保は行いません。従って、配列宣言の段階で[ ]内に数字を書き込み配列要素の数を指定するとコンパイルエラーになります。

配列の領域割当て
new演算子を使い、int型の値またはint 型の値を返す式により要素の数を指定して配列用の領域を確保します。

array = new int[要素の数];

なお、配列の宣言と領域の割当てを同時に行うことは可能です。

int[] array = new int[3];

また、配列の宣言と領域の割当てを同時に行う場合に限り、new演算子による領域確保の代わりに、配列初期化子を使った初期化によって直接配列用の領域を確保することもできます

int[] array = { 1, 2, 3 };

配列初期化子には、リテラル値のほか式も指定できるので、配列要素としてインスタンス生成式を記述しオブジェクトの参照を配列に格納することもできます。

なお、処理の途中で配列変数に配列初期化子で値を代入するとコンパイルエラーになります。しかし、new演算子を使った領域割り当てと同時であれば文中でも配列初期化子による値の代入は可能です。ただし、配列初期化子で要素の数を指定しているので、大カッコの中に要素数を指定するとコンパイルエラーになります。

多次元配列では全ての次元の配列インスタンスを一度に作成する必要はありません。例えば2次元配列では、まず1次元目の配列インスタンスを生成し、2次元目の配列インスタンスはあとから生成し、1次元目の配列の要素に2次元目の配列インスタンスへの参照を代入します。
また、多次元配列の要素を管理するための変数(一次元目の配列インスタンスの要素)は参照先の配列インスタンスの要素数とは無関係なので、2次元目以降の配列の要素数が全て同じである必要もありません

new演算子、初期化演算子による配列の作成
および、段階的な二次元配列の作成

public class InitializationSpecifier {
	public static void main(String args[]) {
		int[] array1 = {1, 2, 3};
		int array2[];
		array2 = new int[]{10, 20, 30};
		int array3[][]= {array1, array2};
		
		System.out.print("array1:");
		for(int ent1: array1) 	System.out.print(ent1 + " ");
		System.out.println();
		System.out.print("array2:");
		for(int ent2: array2) 	System.out.print(ent2 + " ");
		System.out.println();
		System.out.print("array3:");
		for(int[] arr: array3) {
			for(int ent: arr) System.out.print(ent + " ");
		}
	}
}

実行結果
拡張for文による2次元配列からの要素取得法

>java InitializationSpecifier
array1:1 2 3
array2:10 20 30
array3:1 2 3 10 20 30
>

配列要素の設定
配列インスタンスを作成した時に配列要素に設定されるデフォルト値は作る配列の型によって異なります

整数型
0
浮動小数点数型
0.0
真偽型
false
文字型
\u0000
オブジェクト型
null

特にchar型とString型(オブジェクト型)はデフォルト初期値が異なるので注意が必要です。
char型のデフォルト初期値は文字コード\u0000(印字は半角スペース)従って、char型の配列を宣言した場合に取られる要素のデフォルト初期値も\u0000、これに対しString型の配列を宣言した場合に取られる要素のデフォルト初期値はnullです。

配列インスタンスと要素は別物であるため、オブジェクト型配列は注意が必要です。
つまりオブジェクト型配列を作成しただけではオブジェクト型要素の初期値はnullですから、要素に特定のインスタンスを代入せずにオブジェクト型要素のフィールド値を参照すると、実行時に参照先がないという意味のNullPointerExceptionが発生します。

char型の配列とString型配列のデフォルト初期値の確認

public class InitValue {
	public static void main(String args[]) {
	
		char[] charArray = new char[3];
		System.out.print("charArry:[");
		for(char charEnt : charArray) {
			System.out.print(charEnt);
		}
		System.out.println("]");
		
		String[] strArray = new String[3];
		System.out.print("strArry:[");
		for(String strEnt : strArray) {
			System.out.print(strEnt);
		}
		System.out.println("]");

	}
}

実行結果

>java InitValue
charArry:[   ]
strArry:[nullnullnull]

>

char型の配列ではデフォルト初期値\u0000が半角スペースで表示され、String型配列では初期値のnullを文字として表示します。
(printlnメソッドがnull値を文字として扱う仕様です。)

プログラミングに戻る

制御文

制御文とは処理の流れを制御するための文です。
処理の流れを制御するといっても、自由気ままに処理が定義出来れば良いというわけではありません。「構造化プログラミング」では処理は「順次」、「分岐」、「繰返し」の3種類の組み合わせで定義すべきと推奨されています。
それ以外の「移動(goto文)」などを使うとプログラムはいわゆるスパゲティ状態になり、作者以外はもちろん作者でさえも時間が経つと処理の流れを把握しずらくなってしまうといわれます。

順次」は何々をしたら、次に何をする、という処理を次々に行う形態で、コーディングは命令文を順次書き連ねていきます。
分岐」は何々の条件では何を行い、その条件でなければ何を行うという形態で、コーディングはif-else文if-else if文switch文を使って命令文を区分けして書きます。
繰返し」はある条件が有効な間は何を行い続け、条件が有効で無くなったら次の命令に移るという形態で、コーディングはwhile文do-while文for文拡張for文を使って記述します。

言語仕様として難しい点はありませんが、「分岐」と「繰返し」には注意すべき点がいくつかあります。

for文

書式

	for(初期化文; 繰返し条件文; 繰返しが終わる毎に行う更新文) { 繰り返し時に行う処理 }

for文の構文で注意すべき点は、
for文の初期化文、条件文、更新文のうち、初期化文と更新文は複数記述できますし、省略することも出来ます。条件文は一つしか記述できず省略することはできません。複数の条件が必要な場合は論理演算子を使って複合条件にします。
初期化文で複数の変数を宣言する場合、変数は同じ型でなければいけません。異なる型の変数を複数宣言すると、コンパイルエラーが発生します。
初期化文で宣言した変数のスコープはfor文のブロック内であり、for文のブロック外で参照するとコンパイルエラーになります。
繰返し条件の評価は繰返し処理の前に行われるので、条件式の指定を誤ると繰返し処理を一度も行わない場合もあります。

条件式の指定を誤った場合

public class ForSample {
	public static void main(String args[]) {
	
		for(int i=0; i<0; i++) {
			System.out.println("forループの実行ブロック");
		}
		System.out.println("ここはforループの外です");
	}
}

実行結果
繰返し処理を行っていません。

>java ForSample
ここはforループの外です

>

到達不能文
if文やWhile文、for文では条件として論理値を記述する場合がありますが、論理値としてfalseを記述すると命令文を全く実行しなくなってしまいます。Javaではこのような到達不能文が発生すると多くの場合コンパイルエラーを出します。
確かにWhile文、for文ではコンパイルエラーが出ます、しかしif文ではコンパイルエラーは出ません
この処理の違いはif文の場合、下記のような記述により定数DEBUGをtrue、falseに切り替えて利用する為と想像できます。

	if(DEBUG) System.out.println("This is Debug write.");

拡張for文
拡張for文は、ある型のデータ集合から順にデータを呼び出す便利な構文です。しかし、その性質からデータを降順で呼び出したり、一つおきに呼びだすという事は出来ません。従って基本型を収納した一次元配列では標準for文に対する利点は単に記述が簡素化されるだけです。
逆にデータを呼び出すインデックスを指定する必要がないので、標準for文では扱えないHashMap等のint型のインデックスを持たないデータ集合を扱うことが出来ます

if-else if文
Javaの文は句の句の間で空白だけでなく、改行を挿入してもコンパイルエラーにはなりません。しかし、これは改行を置いた場合の解釈がどう扱われるかという事とは別です。
if-else if文の場合elseとifの間に改行を置くと、if-else if文はif-else文として解釈され、改行後のifはelse文の中のif文として解釈されます。

if~else if文のelseの後に改行がある例

public class IfElseSample {
	public static void main(String args[]) {
	
		int num = 1;
		if(num != 1)
			System.out.println("ここは印字されません");
		else if(num < 1)
			System.out.println("ここは印字されません");
		else
			if(num == 1)
				System.out.println("ここは印字されます");
		else
			if(num == 1)
				System.out.println("ここは印字されません");

	}
}

実行結果

>java IfElseSample
ここは印字されます

>

break文、continue文
for文やwhile文、do-while文ではbreak文やcontinue文を使って細かな分岐や繰返し制御が行えます。
break文はその場で実行中のループ抜けます。
ネストされたループの場合はbreak文の置かれたレベルの繰返しを終わるのであって、外側にも別の繰返しがあるケースですべての繰返しを終わりたい場合は外側の繰返しの先頭にラベルを置いて、break文でそのラベルを記述します。

loopExit:	for(int i=0; i<array.length; i++) {
				for(int J=0; j<array[i].length; j++) {
					if(array[i][j] == 99) break loopExit;
				}
			}

continue文はcontinue文以降の命令文をスキップして次の繰返し処理を継続実行します。continue分もラベルを使って処理の継続先を指定することが出来ます。

do-while文
doのあとにはセミコロンがなく実行ブロックが続きます。実行ブロックを{ }で括らない場合は、実行文は1文で2文以上を書くとコンパイルエラーになります。

プログラミングに戻る