ループ条件を満足する代わりにbreak
ステートメントを使用してループを終了することが「悪い習慣」であるかどうか疑問に思っていましたか?
JavaとJVMでループがどのように処理されるかを知るのに十分な洞察がないので、そうすることで重要な何かを見落としているのではないかと思っていました。
この質問の焦点:特定のパフォーマンスオーバーヘッドはありますか?
いい主よ論理的なループ条件を満たさずに、全体的な要件を満たすループで何かが発生する可能性があります。その場合、break
を使用して、ループを無意味に循環させないようにします。
例
String item;
for(int x = 0; x < 10; x++)
{
// Linear search.
if(array[x].equals("Item I am looking for"))
{
//you've found the item. Let's stop.
item = array[x];
break;
}
}
この例でもっと意味があるもの。あなたがそれを見つけた後でも、毎回10までループを続けるか、アイテムを見つけて停止するまでループしますか?または、それを実世界の用語に入れます。鍵を見つけたら、見続けていますか?
コメントに応じて編集
ループを中断するためにx
を11
に設定しないのはなぜですか?無意味です。 break
があります!コードでx
が10
よりも確実に大きいと仮定していない限り(おそらくそうではないはずです)、break
を使用するだけで問題ありません。
完全を期して編集する
break
をシミュレートする方法は他にもあります。たとえば、ループ内の終了条件にロジックを追加します。それは無意味にループするか、break
を使用すると言うのは公平ではありません。指摘したように、whileループは多くの場合、同様の機能を実現できます。たとえば、上記の例に従ってください。
while(x < 10 && item == null)
{
if(array[x].equals("Item I am looking for"))
{
item = array[x];
}
x++;
}
break
を使用するということは、単にfor
ループでこの機能を実現できることを意味します。また、ループの動作を変えたいときはいつでも、終了ロジックに条件を追加し続ける必要がないことを意味します。例えば。
for(int x = 0; x < 10; x++)
{
if(array[x].equals("Something that will make me want to cancel"))
{
break;
}
else if(array[x].equals("Something else that will make me want to cancel"))
{
break;
}
else if(array[x].equals("This is what I want"))
{
item = array[x];
}
}
次のような終了条件を持つwhile loop
ではなく:
while(x < 10 && !array[x].equals("Something that will make me want to cancel") &&
!array[x].equals("Something else that will make me want to cancel"))
break
を使用すると、実際には他の言語機能と同じように、は、特定のコンテキスト内で、明らかに誤用している悪い習慣になる可能性があります。しかし、いくつかの非常に重要なイディオムは、それなしではコーディングできないか、少なくともはるかに読みにくいコードになります。これらの場合、break
が道です。
言い換えれば、break
またはその他についての全面的な無条件のアドバイスを聞かないでください。 「グッドプラクティス」を文字通り強制するためだけにコードが完全に衰弱するのを見たことは一度もありません。
パフォーマンスのオーバーヘッドに関する懸念については、まったくありません。とにかく、バイトコードレベルでは、明示的なループ構造はありません。すべてのフロー制御は、条件付きジャンプの観点から実装されています。
JLSは、ブレークがループの異常終了であることを指定しています。ただし、異常と見なされるからといって、多くの異なるコード例、プロジェクト、製品、スペースシャトルなどで使用されないわけではありません。JVM仕様には、パフォーマンスの低下の有無は記載されていません。ループの後、クリアコードの実行が続行されます。
ただし、コードの可読性は、奇妙な中断を伴う可能性があります。副作用や奇妙なクリーンアップコードに囲まれた複雑なifステートメントにブレークを付けている場合、おそらくラベル付きのマルチレベルブレーク(さらに悪いことに、終了条件の奇妙なセットが次々と続く)を使用すると、誰にとっても読みやすい。
繰り返し変数を繰り返しの範囲外に強制することでループを中断したい場合、または不要な直接的な終了方法を導入することでループを中断したい場合、break
よりも読みにくくなります。
ただし、空の方法で余分にループするのはalmostです。余分な反復が必要であり、不明確になる可能性があるため、常に悪い習慣です。
私の意見では、一定量の反復が行われ、すべての反復が完了する前に停止しない場合は、For
ループを使用する必要があります。先に終了したい場合は、While
ループを使用することを好みます。たとえこれらの2つの小さな単語を読んだとしても、より論理的に思えます。いくつかの例:
for (int i=0;i<10;i++) {
System.out.println(i);
}
このコードをすばやく読むと、確実に10行が印刷されてから続行されます。
for (int i=0;i<10;i++) {
if (someCondition) break;
System.out.println(i);
}
これはすでに私にはあまり明確ではありません。なぜ最初に10回の反復を行うと宣言するのに、ループ内にいくつかの追加条件を追加して、より早く停止するのですか?
私はこの方法で書かれた前の例を好みます(それはもう少し冗長ですが、それはもう1行だけです):
int i=0;
while (i<10 && !someCondition) {
System.out.println(i);
i++;
}
このコードを読む人は誰でも、ループをより早く終了する可能性のある追加の条件があることをすぐに理解できます。
もちろん、非常に小さなループでは、すべてのプログラマーがbreakステートメントに気付くことを常に議論できます。しかし、私は自分の経験から、大きなループではこれらのブレークが監視できることを知ることができます。 (そして、別のトピックに移動して、コードを小さなチャンクに分割し始めます)
いいえ、特定の条件に到達した(一致が見つかった場合)の場合にループを抜けることは悪い習慣ではありません。多くの場合、既に目的を達成しているため、反復を停止する必要がある場合があります。これ以上反復する必要はありません。ただし、誤って何かを見逃したり、必要のないときに脱出したりしないように注意してください。
これはパフォーマンスの向上に追加ループを中断した場合、ループの目的が完了していても(つまり、必要なレコードに一致することが既に行われている場合でも)数千のレコードを繰り返すのではありません。
例:
for (int j = 0; j < type.size(); j++) {
if (condition) {
// do stuff after which you want
break; // stop further iteration
}
}
ループのブレークを使用することは完全に合法であり、いくつかの問題を解決する唯一の方法ですらあります。
しかし、新しいプログラマーは通常それを乱用し、特に最初にループ条件ステートメントに記述された可能性のある条件でループを停止するためにブレークを使用することにより、コードを混乱させるという事実から悪い評判を得ています。
このようなことを始めると、少し奇妙になり始め、returns
matchedConditionの結果になる別のメソッドに移動した方がいいと思います。
boolean matched = false;
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++) {
if(matchedCondition) {
matched = true;
break;
}
}
if(matched) {
break;
}
}
上記のコードをクリーンアップする方法を詳しく説明するには、リファクタリングして、returns
を使用する代わりにbreaks
の関数にコードを移動します。これは一般に、複雑/乱雑なbreaks
をより適切に処理します。
public boolean matches()
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++) {
if(matchedCondition) {
return true;
}
}
}
return false;
}
ただし、以下の例のような単純なものについては。必ずbreak
!を使用してください!
for(int i = 0; i < 10; i++) {
if(wereDoneHere()) { // we're done, break.
break;
}
}
そして、条件を変更すると、上記の場合のi
とj
の値は、コードを非常に読みにくくします。また、上限(この例では10)が変数であるため、ループを終了するために設定する値を推測するのはさらに困難になります。もちろんi
とj
をInteger.MAX_VALUEに設定することもできますが、これはすぐに乱雑になり始めると思います。 :)
break
がアルゴリズムを表現する最も自然な方法である多くの一般的な状況があります。これらは「ループアンドハーフ」構造と呼ばれます。パラダイムの例は
while (true) {
item = stream.next();
if (item == EOF)
break;
process(item);
}
これにbreak
を使用できない場合は、代わりに繰り返す必要があります。
item = stream.next();
while (item != EOF) {
process(item);
item = stream.next();
}
同様に、continue
には、次のような一般的なパターンがあります。
for (item in list) {
if (ignore_p(item))
continue;
if (trivial_p(item)) {
process_trivial(item);
continue;
}
process_complicated(item);
}
これは、特にelse if
が複数の関数呼び出しよりも多い場合に、連鎖process_complicated
を使用した場合よりも読みやすい場合があります。
参考資料: ループの終了と構造化プログラミング:討論の再開
悪い習慣ではありませんが、コードを読みにくくすることができます。これを回避するための便利なリファクタリングの1つは、ループを別のメソッドに移動してから、ブレークの代わりにreturnステートメントを使用することです。たとえば、次のようになります(@Chrisの回答から抜粋した例)。
String item;
for(int x = 0; x < 10; x++)
{
// Linear search.
if(array[x].equals("Item I am looking for"))
{
//you've found the item. Let's stop.
item = array[x];
break;
}
}
これをリファクタリングできます( extract method を使用):
public String searchForItem(String itemIamLookingFor)
{
for(int x = 0; x < 10; x++)
{
if(array[x].equals(itemIamLookingFor))
{
return array[x];
}
}
}
周囲のコードから呼び出されると、より読みやすくなります。
break
およびcontinue
は、多くの場合便利ですが、リーダーの読みやすさを損ないます。 「goto」の概念ほどではありませんが、ほとんど。
さらに、Scala(JavaやOcamlなどの関数型プログラミング言語に触発された)などの新しい言語を使用すると、break
およびcontinue
消えた。
特に関数型プログラミングでは、このスタイルのコードは回避されます。
なぜscalaはbreak and continueをサポートしないのですか?
まとめると:break
とcontinue
は、命令型スタイルのためにJavaで広く使用されていますが、関数型プログラミングの練習に使用したコーダーにとっては奇妙なことかもしれません。
いいえ、それは悪い習慣ではありません。これが最も簡単で効率的な方法です。
Breakを使用することは悪い習慣ではなく、多くの優れた用途がありますが、それだけに頼る必要はありません。ほとんどすべてのブレークの使用は、ループ条件に書き込むことができます。実際の条件を使用するとコードははるかに読みやすくなりますが、長時間実行されるループや無限ループの場合、ブレークは完全に理にかなっています。上に示すように、データを検索するときにも意味があります。
ループを停止する必要がある場所が事前にわかっている場合は、for
、while
、または`do-while
ループで条件を記述することで、コードの可読性が向上する可能性があります。
それ以外の場合は、break
の正確な使用例です。