Java : while文の基本 (文法)
while文は、プログラムを繰り返し処理するための、もっとも原始的な方法です。
より優れた方法もいろいろあります。特に 拡張for文 (for-eachループ文) はおすすめです。
とはいえ、while文が必要となるケースもあります。
本記事では、そんな while文の基本的な使い方をご紹介します。
while文
while文は、プログラムを繰り返し処理させるための制御文です。
while (条件式) {
... ループ文 ...
}
これが基本の形となります。
条件式が true の間はループ文を繰り返し実行します。
コード例を見てみましょう。
※System.out.println は、値をコンソールに出力するためのメソッドです。
var num = 0;
while (num < 3) {
num++;
System.out.println("num = " + num);
}
// 結果
// ↓
//num = 1
//num = 2
//num = 3
条件式は
num < 3
の部分です。
条件式をチェックするときに num < 3 であれば、ループ文を実行します。
ループ文は
num++;
System.out.println("num = " + num);
の部分です。
num 変数を 1加算して、num 変数の値をコンソールに出力させています。
処理の流れは次のようになります。
- num 変数を 0 で初期化。
- while文開始
- 条件式をチェック。num = 0 なので 0 < 3 ... 結果は true、ループ文を実行します。
- ループ文を実行。num変数 に 1を加算してコンソールに出力します。
- 条件式をチェック。num = 1 なので 1 < 3 ... 結果は true、ループ文を実行します。
- ループ文を実行。num変数 に 1を加算してコンソールに出力します。
- 条件式をチェック。num = 2 なので 2 < 3 ... 結果は true、ループ文を実行します。
- ループ文を実行。num変数 に 1を加算してコンソールに出力します。
- 条件式をチェック。num = 3 なので 3 < 3 ... 結果は false、while文を終了します。
- while文終了
結果として、コンソールには
num = 1
num = 2
num = 3
と出力されます。
注意する点としては、num 変数が 3 になった瞬間に while文が終わるわけではない、ということです。
あくまで条件式がチェックされるタイミングで num < 3 であれば while文は終了します。
フローチャート図で書くと次のようになります。
それでは、もう少し詳しく見ていきましょう。
条件式
while (条件式) {
... ループ文 ...
}
条件式は、結果が boolean(真偽値) となる 式 のみ許容します。
boolean にならないものはコンパイルエラーとなります。
例えば < や > による大小の比較や、== による比較は、結果が boolean となる式です。
// 式 : 5 > 3
final var exp1 = 5 > 3;
System.out.println(exp1); // true
// 式 : 4 < -1
final var exp2 = 4 < -1;
System.out.println(exp2); // false
// 式 : 10 == 10
final var exp3 = 10 == 10;
System.out.println(exp3); // true
// コンパイルOK
var num = 5;
while (num > 3) {
...
}
while (num < -1) {
...
}
while (num == 10) {
...
}
もしくは、結果が boolean となるメソッドの呼び出しも OK です。
例えば、文字列の比較に使う equals メソッドは boolean を返します。
final var exp1 = "abc".equals("abc");
System.out.println(exp1); // true
final var exp2 = "abc".equals("xyz");
System.out.println(exp2); // false
// コンパイルOK
var str = "abc";
while (str.equals("xyz")) {
...
}
複数の条件式を使いたい場合は && や || といった論理演算も使えます。
final var exp1 = 5 > 3;
System.out.println(exp1); // true
final var exp2 = "abc".equals("xyz");
System.out.println(exp2); // false
final var exp3 = exp1 && exp2;
System.out.println(exp3); // false
final var exp4 = exp1 || exp2;
System.out.println(exp4); // true
// コンパイルOK
var num = 5;
var str = "abc";
while (num > 3 && str.equals("xyz")) {
...
}
while (num > 3 || str.equals("xyz")) {
...
}
足し算、引き算など + や - を使った式は、結果が数値となります。
boolean ではないので while文の条件式には使えません。
// 式 : 5 + 3
final var exp1 = 5 + 3;
System.out.println(exp1); // 8
// 式 : 4 - 1
final var exp2 = 4 - 1;
System.out.println(exp2); // 3
// コンパイルエラー
var num = 5;
while (num + 3) {
...
}
while (num - 1) {
...
}
補足
- Java以外のプログラミング言語には、条件式に boolean 以外を許容しているものもあります。
例えば C言語では、条件式に数値も許容しています。 0 は false、それ以外は true として扱います。 - しかし、Java では厳密に boolean のみを許容します。0 や 1 といった数値を条件式に使うとコンパイルエラーになります。
break文
while (条件式) {
... ループ文 ...
if (...) {
break;
}
}
さて、今までのコード例では、while文を終わらせるには条件式を false にする必要がありました。
しかし、条件式以外の任意の箇所で while文から抜けたい (終わらせたい) ことがあるかもしれません。
そんなときに使えるのが break文です。
break文は、ループ文の中で次のように書きます。
break;
コード例で見てみましょう。
var num = 0;
while (true) {
System.out.println("------");
System.out.println("loop : start");
num++;
System.out.println(" num = " + num);
if (num == 3) {
System.out.println("Break!");
break;
}
System.out.println("loop : end");
}
// 結果
// ↓
//------
//loop : start
// num = 1
//loop : end
//------
//loop : start
// num = 2
//loop : end
//------
//loop : start
// num = 3
//Break!
今回は、while文を break文で終わらせたいので、条件式には true を直接指定しています。
これで、条件式では while文は終わらなくなりました。
while (true) {
...
}
ループ文では num 変数を 1 ずつ加算します。
そして、num が 3 になったときに "Break!" と出力して break文 で while文を抜けます。
System.out.println("------");
System.out.println("loop : start");
num++;
System.out.println(" num = " + num);
if (num == 3) {
System.out.println("Break!");
break;
}
System.out.println("loop : end");
結果は次のように出力されます。
------
loop : start
num = 1
loop : end
------
loop : start
num = 2
loop : end
------
loop : start
num = 3
Break!
ここで注目したいのが、num = 3 のときのループ処理では、最後の "loop : end" が出力されない ことです。
break文で while文を抜けると、break以降の処理は実行されません。
continue文
while (条件式) {
... ループ文 ...
if (...) {
continue;
}
}
continue文を使うと、ループ文の処理をそこでやめて while文の開始処理へジャンプする、という制御ができます。
continue文は、ループ文の中で次のように書きます。
continue;
文章では少しわかりにくいかもしれません。
コード例で見てみましょう。
var num = 0;
while (num < 4) {
System.out.println("------");
System.out.println("loop : start");
num++;
System.out.println(" num = " + num);
if (num % 2 == 0) {
System.out.println("Continue!");
continue;
}
System.out.println("loop : end");
}
// 結果
// ↓
//------
//loop : start
// num = 1
//loop : end
//------
//loop : start
// num = 2
//Continue!
//------
//loop : start
// num = 3
//loop : end
//------
//loop : start
// num = 4
//Continue!
条件式は num < 4 です。
while (num < 4) {
...
}
ループ文では num 変数を 1 ずつ加算します。
そして、num が偶数のとき (num % 2 == 0) は "Continue!" と出力して continue文 を実行します。
System.out.println("------");
System.out.println("loop : start");
num++;
System.out.println(" num = " + num);
if (num % 2 == 0) {
System.out.println("Continue!");
continue;
}
System.out.println("loop : end");
結果は次のように出力されます。
------
loop : start
num = 1
loop : end
------
loop : start
num = 2
Continue!
------
loop : start
num = 3
loop : end
------
loop : start
num = 4
Continue!
num が奇数のときは continue文は実行されません。
そのため、ループ処理の最初で "loop : start" と出力され、ループ処理の最後で "loop : end" と出力されます。
num が偶数のときは continue文が実行されます。
continue文が実行されると、すぐに while文の開始処理へジャンプします。"loop : end" は出力されません。
無限ループに注意
while文を使うときは、無限ループに注意しましょう。
もし無限ループが起きてしまうと、不具合の症状の1例としては、アプリは反応がなくなりフリーズしてしまいます。
アプリのクラッシュと並んで、致命的なバグの1つですね。
次のコード例を見てみましょう。
double num = 1.0;
while (num > 0.0) {
num *= 10;
System.out.println(num);
}
num は浮動小数点数(double)です。
num が 0 より大きい間、ループを続けます。
そして、ループ文では num を 10倍していきます。
出力は次のようになります。
10.0
100.0
1000.0
10000.0
100000.0
1000000.0
... 省略 ...
9.999999999999999E306
9.999999999999998E307
Infinity
Infinity
Infinity
... 省略 ...
num の値が、10 → 100 → 1000 → 10000 ... と増えているのがわかりますね。
さらに続けていくと、最終的には正の無限大(Infinity) となります。
Infinity は、そこからいくら 10倍しても Infinity のままです。
そして Infinity は 0 より大きいので、この while文は永遠に実行し続けることになります。
これが無限ループです。
while文を使うときは、条件式や break文で、きちんとループが終了するかを常に気にかけなければなりません。
これがなかなか神経を使うのですよね…
もし 拡張for文 (for-eachループ文) が使えるケースであれば、そちらを使うことをおすすめします。
拡張for文は、なんと条件式を使わずにループ処理が可能です。(その代わり使えるケースは限定的です)
{ } の省略
while (条件式)
ループ文
ループ文が 1つの文 のみの場合は { } を省略することができます。
var num = 0;
// 通常の書きかた
while (num < 3) {
num++;
}
// こう書いたり…
while (num < 3)
num++;
// こう書くこともできます。
while (num < 3) num++;
ただし、{ } を省略すると、思わぬコーディングミスを誘発してしまうかもしれません。
次のコード例を見てみましょう。
var num = 0;
while (num < 3) System.out.println(num); num++;
さて、ぱっと見て、どのような結果になるかわかりますでしょうか。
実は、この while文は無限ループします。
コンソールには
num = 0
num = 0
num = 0
num = 0
num = 0
...
と、"num = 0" が出力され続けます。
なぜこのような結果になるのか考えてみましょう。
while文の { } を省略したので、ループ文は
System.out.println(num);
の 1つの文 だけが対象となります。
その後ろの
num++;
の文は、while文の外側となります。
つまり次のコードと同じ意味になります。
var num = 0;
while (num < 3) {
System.out.println("num = " + num);
}
num++;
num 変数は while文で変わることはないので、いつまでも条件式は false にならず無限ループしてしまう…というわけでした。
{ } がないと、どこまでが ループ文なのか分かりにくくなることがあります。
個人的には、1つの文のみの場合でも { } は省略しないほうがよいんじゃないかな…と思います。
do-while文
while文と似た制御文に、do-while文があります。
do {
... ループ文 ...
} while (条件式);
do-while文は、条件式がループ文の後ろにきます。
var num = 0;
do {
num++;
System.out.println("num = " + num);
} while (num < 3);
// 結果
// ↓
//num = 1
//num = 2
//num = 3
フローチャートでは次のようになります。
do-while文は、条件式によらずにループ文を必ず1回は実行したい、というときに便利なことがあります。
次のコードは、条件式が最初から false になる例です。
while文と do-while文の制御の違いが確認できるかと思います。
while文の例 | do-while文の例 |
---|---|
|
|
break文や continue文については、while文と同じように do-while文でも使えます。
代替えとなる方法
Java言語には、while文の代わりとなるものがいくつか用意されています。
ここではそれらを簡単にご紹介します。
基本for文
基本for文は、変数を使ってループ回数を制御するのに優れた、繰り返し制御文です。
for (初期化処理; 条件式; 更新処理) {
... ループ文 ...
}
これが基本の形となります。
処理の流れは次のようになります。
- for文開始
- 初期化処理を実行。
- 条件式を判定。true であればループ文を実行。false であれば for文は終了。
- ループ文を実行。
- 更新処理を実行。
- 条件式の判定に戻る。
- 初期化処理を実行。
- for文終了
コード例です。
for (int i = 0; i < 5; i++) {
System.out.println("i = " + i);
}
// 結果
// ↓
//i = 0
//i = 1
//i = 2
//i = 3
//i = 4
これは while文を使って次のように記述するのと同じです。
{
int i = 0;
while (i < 5) {
System.out.println("i = " + i);
i++;
}
}
// 結果
// ↓
//i = 0
//i = 1
//i = 2
//i = 3
//i = 4
基本for文については、よろしければ下記の記事もご参照ください。
拡張for文
for (T value : "配列" か "Iterable" を実装したクラス) {
...
}
拡張for文は、配列 もしくは Iterable を実装したオブジェクトに対して、繰り返し処理を行います。
例えば List は Iterable を実装しています。
条件式は必要とせず、すべての要素に対して繰り返し処理が実行できます。
Iterable は基本有限なので、無限ループの心配はほぼありません。 (実装次第では無限にもできますが…)
List の要素に順次アクセスして出力する例です。
final var list = List.of("aaa", "bbb", "ccc");
for (final var value : list) {
System.out.println("value = " + value);
}
// 結果
// ↓
//value = aaa
//value = bbb
//value = ccc
これは while文を使って次のように記述するのと同じです。
final var list = List.of("aaa", "bbb", "ccc");
final var it = list.iterator();
while (it.hasNext()) {
final var value = it.next();
System.out.println("value = " + value);
}
// 結果
// ↓
//value = aaa
//value = bbb
//value = ccc
拡張for文については、よろしければ下記の記事もご参照ください。
Stream API
拡張for文をさらに発展させたものとして、Stream API があります。
こちらは少し難易度が高めなので、Javaに慣れてからでよいとは思います。
Stream API を使って List の要素を大文字に変換してから出力する例です。
final var list = List.of("aaa", "bbb", "ccc");
list.stream().map(String::toUpperCase).forEach(value -> {
System.out.println("value = " + value);
});
// 結果
// ↓
//value = AAA
//value = BBB
//value = CCC
Stream API については下記の記事で詳しく解説していますので、興味がありましたらご参照ください。
公式ドキュメント
さらに詳しく while文について知りたい場合は、公式のJava言語仕様に記載があります。
(残念ながら公式の日本語訳はないようです)
まとめ
while文を使うと、プログラムを繰り返し処理させることができます。
ただし、場面によっては while文より優れた方法もあります。
例えば、基本for文や拡張for文です。
個人的には、while文が必要となるケースは少なく感じます。
拡張for文、Stream API で済むことも多いです。
while文を理解できたら、ぜひ次は、拡張for文や Steream API も理解を深めましょう。
関連記事
- ストリームの基本 (Stream)
- if文の基本
- for文の基本
- プリミティブ型 (基本データ型)
- リテラルの表記方法いろいろ
- クラスの必要最低限を学ぶ
- インタフェースの default メソッドとは
- インタフェースの static メソッドの使いどころ
- var (型推論) のガイドライン
- アクセス修飾子の基本
- 配列 (Array) の使い方
- 拡張for文 (for-eachループ文)
- switch文ではなくswitch式を使おう
- try-with-resources文でリソースを自動的に解放
- テキストブロックの基本
- 列挙型 (enum) の基本
- ラムダ式の基本
- レコードクラスの基本 (Record Class)
- メソッド参照の基本
- シールクラスの基本(Sealed Class)
- 無名変数の使い方