カウンターがないとき
for ループは、あらかじめ用意したカウンターを中心に組み立てられています。しかし、きれいな回数を持たないループもたくさんあります。ファイルが終わるまで行を読み続ける、正しくなるまでパスワードを尋ね続ける、1 になるまで数を半分にし続ける、などです。こうした場合には while ループが自然にフィットします。条件が真であり続ける間、ただ繰り返すだけです。
while ループは、いちばん最初を含め、毎回のパスの前に条件をチェックします。最初から条件が偽なら、本体は一度も実行されません。
条件 count > 0 がまずチェックされ、真なら本体が実行され、その後ループの先頭に戻ってもう一度チェックします。count-- の行が、最終的に条件を偽にするものです。これを削ると、ループは永遠に回り続けます。
while ループの構造
すでに知っている 3 部構成の for ループと比べてみましょう。while ループはその 3 つの役割を分解します。セットアップはループの前に行い、条件は括弧の中に置き、更新は本体の中に置きます。
int i = 0; // 初期化 - ループの前
while (i < 5) { // 条件 - 毎回のパスでチェックされる
System.out.println(i);
i++; // 更新 - これは自分で覚えておかなければならない
}
その最後の部分が落とし穴です。for ループでは更新が条件のすぐ隣に置かれるため、忘れにくくなっています。while ループでは、それは本体の中の単なる 1 つの文にすぎません。最もよくあるバグは、これを書き忘れてプログラムをハングさせることです。
do-while: 本体を最低 1 回実行する
繰り返すかどうかを判断する前に、まず一度本体を実行する必要があるときがあります。do-while ループは末尾で条件をチェックするので、本体は必ず最低 1 回は実行されます。
入力が有効かどうかを知る前に、最低一度はプロンプトを出して読み取らなければなりません。これこそまさに do-while が与えてくれるものです。while (...) の後のセミコロンに注意してください。これは必須で、忘れることはよくあるコンパイルエラーです。
ふつうの while との違いは、条件が最初から偽のときにはっきり現れます。
出力されるのは do-while body だけです。while ループは、最初のチェックの前に x < 5 が偽だったため、本体を完全にスキップします。
番兵値までループする
while の典型的な使い方は、特別な「停止」値、すなわち番兵(sentinel)まで読み取ることです。ここにはカウンターがなく、データが止まれと告げるまでひたすら続けます。
パターンは、ループの前に一度読み、その後は各パスの末尾でもう一度読むというものです。こうすれば条件は常に新しい入力を評価します。本体の中の 2 回目の読み取りを忘れると、value は決して変化せず、無限ループになります。
while ループでの break と continue
break と continue は、ここでも for ループと同じように動作します。break はすぐにループを抜けます。continue はそのまま条件チェックに戻り、現在のパスの残りをスキップします。
これは 1 3 5 7 9 を出力します。while (true) は意図的に自分からは決して止まりません。唯一の出口は break です。while ループでの continue には注意してください。更新が本体の一部であるため、カウンターを進める前に continue で先頭に戻るのは、ループを静かにハングさせる手です。上の例では n++ が先に実行されるので安全です。
無限ループに気をつける
条件は最終的に偽にならなければならず、それは本体の中の何かが変化することに完全に依存します。よくある 2 つの原因は、更新を忘れることと、間違ったやり方で更新することです。
int i = 0;
while (i < 5) {
System.out.println(i); // i が決して変化しない -> 永遠に回り続ける
}
int i = 0;
while (i != 10) {
i += 3; // 0, 3, 6, 9, 12... 10 をちょうど飛び越える -> 永遠に回り続ける
}
最初のものは i が一度も更新されないためハングします。2 つ目は、カウンターが条件の探している正確な値を踏み越えてしまうためハングします。ステップがぴったり当たらない可能性があるときは、!= より < や <= を選びましょう。while (true) は、確実に到達する break があれば問題ありません。意図しないものは単なるバグです。
次へ: for-each ループ
while ループは、条件が変わるまで繰り返し、きれいな回数がないときに適したツールです。しかし、配列やリストのすべての要素をただ訪れたいだけのとき(インデックスも、管理すべき条件もない)には、さらにすっきりしたものがあります。拡張された for-each ループです。それが次のページです。
よくある質問
Java で while と do-while の違いは何ですか?
while ループは最初のパスの前に条件をチェックするため、本体が 0 回しか実行されないこともあります。do-while ループはまず本体を 1 回実行し、その後に末尾で条件をチェックするので、必ず最低 1 回は実行されます。繰り返すかどうかを判断する前に処理を行わなければならない場合(入力を促してから検証する場合など)は do-while を使いましょう。
Java で for ループではなく while ループを使うべきなのはどんなときですか?
きれいなカウンターがなく、単にある条件が変わるまで繰り返したいときは while ループを使います。ユーザーが quit と入力するまで入力を読み取る、値が準備できるまでポーリングする、キューが空になるまで処理する、などです。反復回数が分かっている場合や、数えるべき明白なインデックスがある場合は for ループを使いましょう。
Java で無限の while ループを止めるにはどうすればよいですか?
本体の中の何かが最終的に条件を偽にすることを必ず保証してください(カウンターを減らす、ポインタを進める、フラグを立てるなど)。意図的な while (true) ループの場合は、if でガードした break を中に置きます。ループがハングした場合、よくある原因は条件が依存している変数を更新し忘れることです。