#目的
Java言語を含めたプログラミングの学習を始めたばかりの方、既学習者の方は復習用に、
今回は繰り返し処理、繰り返し制御文について学ぶために書いています。
【Java入門目次】
・変数と型
・型変換
・変数のスコープ
・文字列の操作
・配列の操作
・演算子
・条件分岐
・繰り返し処理 ←今ここ
・クラスについて(準備中)
・抽象クラス(準備中)
・インターフェース(準備中)
・カプセル化(準備中)
・モジュールについて(準備中)
・例外処理について
・ラムダ式について
・Stream APIについて
#繰り返し処理とは
とある処理を条件が成立している間
や回数を指定して
、繰り返し処理をさせる事です。
int a = 0;
System.out.println("a => " + a);
a += 1;
System.out.println("a => " + a);
a += 1;
System.out.println("a => " + a);
上記の様なaを出力、+1して再度出力といった一連の処理も繰り返し処理によってスッキリ記述する事が可能です。
繰り返し処理はループとも呼ばれ、本記事ではwhile文、do-while文、for文、拡張for文
を取り上げます。
#while文
指定された条件が成立する(true)間、繰り返し処理を行います。
while( 条件式 ) {
処理文; // 条件式がtrueの場合、実行される
}
whileの後の条件式は、boolean値(true,false)を返す式
ではないといけません。
int num = 0;
while(num < 5) {
System.out.println("num => " + num);
num++;
}
num => 0
num => 1
num => 2
num => 3
num => 4
冒頭でも出てきた変数を出力、+1して再度出力といった一連の処理をスッキリ記述する事が出来ています。
numが5未満の間この処理は繰り返され
、ループ毎にインクリメントされたnumが5になった時、
条件判定時にfalseが返されるためwhile文は終了します。
##while文の注意点
###無限ループ
繰り返し処理が止まる事なく実行され続けてしまう事があります。
以下の点に注意しましょう。
int num = 0;
while(num < 5) {
System.out.println("num => " + num);
num++;
}
このコードでnum++
の記述がなかった場合、num変数は0のままなので
条件式num < 5
はtrueを返し続けるため、無限ループとなってしまいます。
int num = 0;
while(true) {
System.out.println("num => " + num);
num++;
}
次に、条件式のところでwhile(true)
をしてしまった場合。
これも常にtrueと返されるので無限ループとなってしまいます。
無限ループになってしまわない為(ループから抜ける為)に使われる繰り返し制御文は別記事にて説明します。そちらも一緒に覚えておきましょう。
###ループが始まらない
while(false) {
// 処理
}
条件式をwhile(false)
とすると、処理に入る事が出来ない為コンパイルエラーとなります。
###何も出力されない
エラーにもならないのに何も出力されない時もあります。
int num = 0;
while(num > 0) {
System.out.println("num => " + num);
num--;
}
上記の実行結果は何も出力されません。
whileの条件が0より大きいかを比較している為、いきなりfalseが返されループの中に入る事なく処理が終わってしまうからです。
num変数を1以上にするとループ内の処理が実行されます。
int num = 3;
while(num > 0) {
System.out.println("num => " + num);
num--;
}
num => 3
num => 2
num => 1
#do-while文
while文と同様に指定された条件が成立する(true)間、繰り返し処理を行います。
do {
処理文;
}while( 条件式 );
while文と同様で条件式は、boolean値(true,false)を返す式
ではないといけません。
int num = 0;
do {
System.out.println("num => " + num);
num++;
}while(num < 5);
num => 0
num => 1
num => 2
num => 3
num => 4
numが5未満の間この処理は繰り返され
、ループ毎にインクリメントされたnumが5になった時、
条件判定時にfalseが返されるためdo-while文は終了します。
#while文とdo-while文の違い
条件判定が行われるタイミングが違います。
while文では先に条件判定が行われるので、前述の通り処理が実行されない場合もあります。
しかし、do-while文では条件判定よりも先にdoブロックがあり後で条件判定が行われます。よって条件に関係なく1度は処理が実行されます。
int num = 0;
while(num > 0) {
System.out.println("while_num => " + num);
num--;
}
上記のwhile文では何も出力されません。
int num = 0;
do {
System.out.println("do-while_num => " + num);
num--;
}while(num > 0);
do-while_num => 0
do-while文では1度だけ処理が実行され、条件式がfalseである為do-while文を終了しています。
このような違いがある事に注意しましょう。
#while文とdo-while文の共通点
while文もdo-while文も処理する文が1つしかない場合は{}
を省略して記述する事が可能です。
int num = 0;
while (num < 5) System.out.println("num => " + num++);
while_num => 0
while_num => 1
while_num => 2
while_num => 3
while_num => 4
int num = 5;
do
System.out.println("do-while_num => " + num--);
while(num > 0);
do-while_num => 5
do-while_num => 4
do-while_num => 3
do-while_num => 2
do-while_num => 1
#for文
for文も条件判定がtrueである間、繰り返し処理が実行されます。
while文、do-while文では、()
には条件式のみ記述していましたが、
for文の()
内では、カウント変数の宣言、初期化、条件式、カウント変数の更新を行います。
for(式1; 式2; 式3;) {
処理文;
}
for文の流れは以下の通りになります。
・式1で繰り返し回数を示す変数(カウンタ変数)を宣言、初期化。
・式2で条件判定を行う。
・条件判定がtrueの場合、処理文が実行される。
・処理が実行された後、式3でカウンタ変数の更新が行われる。
・再び式2で条件判定を行う。
・式2での判定がfalseの場合、for文を終了する。
実際にコードと実行結果を見てみましょう。
for(int i = 0; i < 5; i++) {
System.out.println("i => " + i);
}
i => 0
i => 1
i => 2
i => 3
i => 4
このようにfor文でもwhile文、do-while文と同様の処理を記述する事が出来ます。
##for文の省略した記述方法
以下の様な記述方法でもfor文は実行されます。
###カウンタ変数の宣言、初期化の省略
int num = 0;
for(; num < 5; num++) {
System.out.println("num => " + num);
}
num => 0
num => 1
num => 2
num => 3
num => 4
for文でカウンタ変数の宣言、初期化を行うのではなく、事前に行っています。
この様な記述方法でも動作します。
###カウンタ変数の更新の省略
for(int num = 0; num < 5; ) {
System.out.println("num => " + num++);
}
num => 0
num => 1
num => 2
num => 3
num => 4
処理文内にてnum変数をインクリメントして更新しています。
この様な記述方法でも動作します。
インクリメントを忘れてしまうと無限ループになってしまうので注意しましょう。
##for文の様々な記述方法と注意点
for文の()
内に記述する方法はいくつか規則があるのでみていきましょう。
###式1は文である必要がある
以下は式1に変数を記述しているだけなのでエラーになります。
int num = 1;
for(num; num < 5; num++) {
System.out.println("num => " + num);
}
以下の様に式1は文である必要があります。
int num = 1;
for(num += 1; num < 5; num++) {
System.out.println("num => " + num);
}
num => 2
num => 3
num => 4
###式1の宣言式は1つのみ
以下は式1で複数宣言式を記述しているためエラーになります。
for(int i = 0, int = j = 0; i < 5; i++) {
System.out.println("i => " + i);
System.out.println("j => " + (j+=2));
System.out.println("===============");
}
以下の様に式1での宣言式は1つではないといけません。
for(int i = 0, j = 0; i < 5; i++) {
System.out.println("i => " + i);
System.out.println("j => " + (j+=2));
System.out.println("===============");
}
i => 0
j => 2
===============
i => 1
j => 4
===============
i => 2
j => 6
===============
i => 3
j => 8
===============
i => 4
j => 10
===============
###式3も複数入れる事が可能
カンマで式を区切る事で、カウンタ変数の更新処理も複数入れる事が出来ます。
上記のコードを書き換えてみます。
for(int i = 0, j = 2; i < 5; i++, j+=2) {
System.out.println("i => " + i);
System.out.println("j => " + j);
System.out.println("===============");
}
i => 0
j => 2
===============
i => 1
j => 4
===============
i => 2
j => 6
===============
i => 3
j => 8
===============
i => 4
j => 10
===============
同様の結果を出力する事が出来ます。
#拡張for文
for文をさらに便利にした記述方法として、配列やコレクションの全要素を順番に取り出して処理をする場合に使用します。
for(変数宣言 : 参照変数名) {
処理文;
}
実際にコードと実行結果を見てみましょう。
int[] numbers = {1, 2, 3, 4, 5};
for(int val : numbers) {
System.out.println(val);
}
val => 1
val => 2
val => 3
val => 4
val => 5
numbersの要素を1つずつval変数へ代入し出力
という流れを繰り返し行っています。
同じ処理をfor文で記述して比較してみましょう。
int[] numbers = {1, 2, 3, 4, 5};
for(int i = 0; i < numbers.length; i++) {
System.out.println("numbers[" + i + "] => " + numbers[i]);
}
numbers[0] => 1
numbers[1] => 2
numbers[2] => 3
numbers[3] => 4
numbers[4] => 5
拡張for文で記述する事により、カウンタ変数の宣言、初期化や条件式、カウンタ変数の更新処理の記述が不要になります。
for文に比べてシンプルに記述する事が可能です。
##拡張for文の注意点
変数宣言する型は、参照する変数の型と一致していなければいけません。
以下はエラーになります。
String[] names = {"田中", "佐藤", "鈴木"};
for(int val : names) {
System.out.println("name => " + val);
}
参照変数がString型の配列であるのに対して、変数宣言がint型
である為です。
参照変数と各要素を受け取る変数の型は一致合わせるよう注意しましょう。
String[] names = {"田中", "佐藤", "鈴木"};
for(String val : names) {
System.out.println("name => " + val);
}
name => 田中
name => 佐藤
name => 鈴木
#for文と拡張for文の共通点
for文も拡張for文も処理する文が1つしかない場合は{}
を省略して記述する事が可能です。
for(int i = 0; i < 3; i++)
System.out.println("i => " + i);
i => 0
i => 1
i => 2
int[] numbers = {10, 20, 30};
for(int val : numbers)
System.out.println("val => " + val);
val => 10
val => 20
val => 30
ローカル変数型推論(var)の利用が可能です。
(ローカル変数型推論については別記事にまとめようと思います。)
for(var i = 0; i < 3; i++) {
// 省略
}
for(var val : numbers) {
// 省略
}
#繰り返し制御文とは
上述の繰り返し処理は、条件式がtrueの間実行され続けるというものでした。
特定の条件の時に繰り返し処理から抜けたいという時、繰り返し制御文であるbreak文やcontinue文を使用します。
#break文
実行中の繰り返し処理を中断して抜け出す時、繰り返し文の無限ループから抜け出す時に使用します。
switch文のcase内でも使用します。
ループから抜けた後は次の処理を実行します。
for(int i = 0; ; i++) {
if(i == 5) {
break;
}
System.out.println("i => " + i);
}
System.out.println("for文の後の処理");
i => 0
i => 1
i => 2
i => 3
i => 4
for文の後の処理
式2を書き忘れているため、条件式がありません。よって無限ループになってしまいます。
しかし、if(i == 5)
のブロック内にbreak;
がある事で、iが5になった時ループを抜ける事になります。
そしてfor文の後の処理を実行しています。
#continue文
実行中の繰り返し処理を中断するのではなくその処理だけスキップして、条件式の判定を行わせ更に繰り返し処理を続ける時に使用します。
for(int i = 0; i < 10; i++) {
if((i % 3) == 0) {
continue;
}
System.out.println("i => " + i);
}
System.out.println("for文の後の処理");
i => 1
i => 2
i => 4
i => 5
i => 7
i => 8
for文の後の処理
if((i % 3) == 0)
のブロック内にcontinue;
がある事で、
iを3で割って余りが0の時に処理をスキップしています。(式2の条件判定がtrueである限りループは続く)
そして、for文が終了した後はその後の処理を実行しています。
#break文とcontinue文の共通点 ラベル
繰り返し文がネストしている場合(複数ある場合)、内側の繰り返し文に記述されたbreak文やcontinue文は、内側の繰り返し処理のみ適用されます。
外側のループからも抜けたい、スキップしたい場合、ラベルを使用する事で可能です。
ラベル名:
for(式1; 式2; 式3;) {
for(式1; 式2; 式3;) {
break 又は continue ラベル名;
}
}
break文とcontinue文でそれぞれラベルの有無で処理がどう違うかみてみましょう。
##break文の場合
ラベルを使用しない場合は以下の様になります。
iとjが1の時にループを終えたい。という想定で実装をしています。
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
System.out.println("i: " + i + ", j: " + j);
if(i == 1 && j == 1) {
break;
}
}
}
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0
i: 1, j: 1
i: 2, j: 0
i: 2, j: 1
i: 2, j: 2
ループは終わらず、iがインクリメントされて次のループにいっています。
ラベルを使用した場合は以下の様になります。
loop:
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
System.out.println("i: " + i + ", j: " + j);
if(i == 1 && j == 1) {
break loop;
}
}
}
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0
i: 1, j: 1
iとjが1の時に外側のループも抜ける事が出来ています。
##continue文の場合
ラベルを使用しない場合は以下の様になります。
iとjが1の時に、iをインクリメントした次のループへスキップしたい。という想定で実装をしています。
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
if(i == 1 && j == 1) {
continue;
}
System.out.println("i: " + i + ", j: " + j);
}
}
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0
i: 1, j: 2
i: 2, j: 0
i: 2, j: 1
i: 2, j: 2
iとjが1の時だけしかスキップされていません。
ラベルを使用した場合は以下の様になります。
loop2:
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
if(i == 1 && j == 1) {
continue loop2;
}
System.out.println("i: " + i + ", j: " + j);
}
}
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0
i: 2, j: 0
i: 2, j: 1
i: 2, j: 2
iとjが1の時に、iをインクリメントした次のループへスキップ出来ています。
この様にラベルを用いる事で、多重ループから抜け出したり、スキップしたりする事が可能
になります。
#終わりに
同じ処理を繰り返し行うための構文、条件に応じて処理を中断、スキップさせるための繰り返し制御文を学びました。
使い分ける事によってコードをスッキリ記述する事ができ、同じ処理を何度も書かなくて済むので使いこなしていきたいですね。
#参考サイト