Javaでネストされたループから抜け出すにはどうすればよいですか?


1818

私は次のようなネストされたループ構造を持っています:

for (Type type : types) {
    for (Type t : types2) {
         if (some condition) {
             // Do something and break...
             break; // Breaks out of the inner loop
         }
    }
}

どうすれば両方のループから抜け出すことができますか?私は同様の質問を見てきましたが、特にJavaに関係するものはありません。ほとんどの場合gotoを使用したため、これらのソリューションを適用できませんでした。

内側のループを別のメソッドに入れたくありません。

ループを再実行したくありません。ブレークすると、ループブロックの実行が終了します。

回答:


2427

他の回答者と同じように、私は間違いだろう好むあなただけの完全反復を停止するには戻ることができた時点で、別の方法でループを配置します。この回答は、質問の要件を満たす方法を示しています。

break外側のループのラベルと一緒に使用できます。例えば:

public class Test {
    public static void main(String[] args) {
        outerloop:
        for (int i=0; i < 5; i++) {
            for (int j=0; j < 5; j++) {
                if (i * j > 6) {
                    System.out.println("Breaking");
                    break outerloop;
                }
                System.out.println(i + " " + j);
            }
        }
        System.out.println("Done");
    }
}

これは印刷します:

0 0
0 1
0 2
0 3
0 4
1 0
1 1
1 2
1 3
1 4
2 0
2 1
2 2
2 3
Breaking
Done

286
これループの直後にジャンプします。それを試してみてください!はい、ラベルはループの前にありますが、それは、それが終了したい場所ではなく、ループにラベルを付けるためです。(レーベルを続けることもできます。)
Jon Skeet、

2
Perlはこれも独自のラベルシステムを許可します。私は多くの言語がそうだと思います-それがJavaにあることを私はほとんど驚きません。
エヴァンキャロル

9
@Evan-その主張は明らかに真実です-それが真実であることを約束する言語では。しかし、Javaはその約束をしません。言語があなたの仮定と矛盾している場合、それはあなたの仮定が間違っている可能性があります。この場合、私はあなたがまだ部分的に正しいと思います-その形式のことを聞いたことがない(または忘れた)多くのWRTを驚かせないという原則break。それでも、例外は別のよく知られた例外です(申し訳ありません)。しかし、それが明らかでない場合(これは、小さなループ、ラベル/ブレークがまだ十分に表示されない場合の警告コメント)、私はまだこれについて不満があります。
Steve314

6
@MuhammadBabar:outerloopはラベルです。どのコードを試したか正確にはわかりませんが、私の回答のコードはコンパイルして問題なく実行されます。
Jon Skeet、2015

4
@NisargPatil sonarLintにあるからといって、コードの匂いがするわけではありません。これは、これをsonarLintに追加した個人的なかゆみを伴う開発者がいたことを意味します。虐待された場合、または一部の開発者が個人的な憎しみの十字軍を持っている場合にのみ意味を持つこれらの種類のルールが満載です。ラベル付きの区切りと継続は、何をしたいかを説明する非常にエレガントな方法です。
john16384

402

技術的に正しい答えは、外側のループにラベルを付けることです。実際には、内部ループ内の任意の時点で終了したい場合は、コードをメソッド(必要に応じて静的メソッド)に外部化してから呼び出した方がよいでしょう。

それは読みやすさのために報われるでしょう。

コードは次のようになります。

private static String search(...) 
{
    for (Type type : types) {
        for (Type t : types2) {
            if (some condition) {
                // Do something and break...
                return search;
            }
        }
    }
    return null; 
}

受け入れられた回答の例と一致:

 public class Test {
    public static void main(String[] args) {
        loop();
        System.out.println("Done");
    }

    public static void loop() {
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                if (i * j > 6) {
                    System.out.println("Breaking");
                    return;
                }
                System.out.println(i + " " + j);
            }
        }
    }
}

30
場合によっては、内部ループの外側にあるいくつかのローカル変数を使用し、それらすべてを渡すことは不格好に感じることがあります。
Haoest

1
では、この解決策は、受け入れられた回答のように「完了」を印刷することになっているのでしょうか。
JohnDoe

1
@JohnDoe呼び出してSystem.out.println( "done");を出力します。検索メソッド内で{}最後に{}を試すこともオプションです。
Zo72、2012年

2
これは良い方法だと思いますが、中断するのではなく続行したい場合はどうなりますか?ラベルは同じように(またはひどく!)どちらもサポートしますが、継続のためにこのロジックを変換する方法がわかりません。
Rob Grant

@RobertGrantブレークではなく続行する場合は、外側のループをloopメソッドの外側に移動し、メソッドから戻って続行します。
Muhd 2016

215

ループの周りに名前付きブロックを使用できます:

search: {
    for (Type type : types) {
        for (Type t : types2) {
            if (some condition) {
                // Do something and break...
                break search;
            }
        }
    }
}

40
ラベルを使用するために新しいブロックを作成する必要はありません。
ジョンスキート、

81
いいえ、しかしそれは意図をより明確にします。受け入れられた回答の最初のコメントを参照してください。
ボンベ

2
実際には名前付きブロックではありません。ラベルを付けた後、ラベルなしでJava式を記述できますがname: if(...){...}、名前付き条件になりますか?:)
La VloZ Merrill

4
この構成には、for直接のラベル付けよりも大きな利点があります。}条件が満たされない場合にのみ実行される最後のコードの前にコードを追加できます。
フロリアンF

2
これは名前付きブロックであり、名前付きループではありません。そのループ内で「検索を続ける」ことはできませんでした。ループがsearchという名前の場合、これは完全に正当な構文です。それを壊すことはできますが、続行することはできません。
18

132

ラベルは使用しません。入るのは悪い習慣のようです。これが私がすることです:

boolean finished = false;
for (int i = 0; i < 5 && !finished; i++) {
    for (int j = 0; j < 5; j++) {
        if (i * j > 6) {
            finished = true;
            break;
        }
    }
}

4
これの&& !finished代わりでは|| !finishedないですか?そして、なぜ内部ループにもbreak使用せず、まったく使用&& !finishedしないのですか?
ガンダルフ2013

4
breakループを任意に終了できるようにするために使用します。そのifブロックの後にコードがある場合は、実行するbreak前にできます。しかし、あなたは正しいです&&。修正しました。
Elle Mundy 2013

2
素敵な解決策!何らかの理由で追加の関数に配置することが望ましくない場合は、実際にそれを行うのです。
ベンロス2013

6
内部ループの後に何らかのロジックがある場合、潜在的な問題があります...新しいループが開始された場合にのみ実行され続け、外部ループが中断します...
Karthik Karuppannan

7
なぜそんな風にやらなければならないのか分からない。コードを扱う人々は、言語のすべての機能を使用できるはずです。他の人が理解できるコードを書くことは重要ですが、言語によって提供される公式ツールの使用を制限することではなく、同じ機能の回避策を見つけることが重要です。それとも、「悪い習慣」によって他の意味ですか?
codepleb

107

ラベルを使用できます。

label1: 
for (int i = 0;;) {
    for (int g = 0;;) {
      break label1;
    }
}

4
私から+1してください。単純に、要点は、質問に答えます。同時に答えたので、既存の答えを繰り返すと非難することはできません。
Heimdall 2018

40

関数を使用します。

public void doSomething(List<Type> types, List<Type> types2){
  for(Type t1 : types){
    for (Type t : types2) {
      if (some condition) {
         // Do something and return...
         return;
      }
    }
  }
}

20

一時変数を使用できます。

boolean outerBreak = false;
for (Type type : types) {
   if(outerBreak) break;
    for (Type t : types2) {
         if (some condition) {
             // Do something and break...
             outerBreak = true;
             break; // Breaks out of the inner loop
         }
    }
}

関数によっては、内部ループを終了/戻ることもできます。

for (Type type : types) {
    for (Type t : types2) {
         if (some condition) {
             // Do something and break...
             return;
         }
    }
}

7
私はこのように雑然としています。
boutta 2009年

11
ループを通して毎回余分な条件チェック?結構です。
ryandenki

15

breaksとgotos が気に入らない場合は、for-inの代わりに「従来の」forループを使用して、追加の中止条件を指定できます。

int a, b;
bool abort = false;
for (a = 0; a < 10 && !abort; a++) {
    for (b = 0; b < 10 && !abort; b++) {
        if (condition) {
            doSomeThing();
            abort = true;
        }
    }
}

2
foreachループには適していません。
John McClane 2018

1
@JohnMcClane 9年以上前の質問の多くの回答でこれをスパムしているのは...?
Quintec、2018

12

同様のことをする必要がありましたが、拡張forループを使用しないことを選択しました。

int s = type.size();
for (int i = 0; i < s; i++) {
    for (int j = 0; j < t.size(); j++) {
        if (condition) {
            // do stuff after which you want 
            // to completely break out of both loops
            s = 0; // enables the _main_ loop to terminate
            break;
        }
    }
}

状態が壊れた後、すべてのアイテムを反復処理するのはクールではありません。したがって、elseの場合は休憩を追加します。
boutta

@bouttaこの結論にどのように到達しているかはわかりません。条件が真になると、両方のループが終了します。
Swifty McSwifterton 2016

OK、私は 's'変数の操作でその部分を取得しませんでした。しかし、sはサイズを表しているので、そのような悪いスタイルを見つけます。それから私は、明示的なVARSとddyerから答えを好む:stackoverflow.com/a/25124317/15108
boutta

@boutta s以下の値にi変更iすることもs、それ以上の値に変更することもできます。どちらの方法でもうまくいくはずです。変更についてはs、後で他の場所で使用できるので正しいですが、変更してiも害forはありません。最初のループが続行されないようにするためです。
Zsolti 2017

9

ループテストに明示的な「exit」を追加することを好みます。カジュアルな読者であれば、ループが早期に終了する可能性があることは明らかです。

boolean earlyExit = false;
for(int i = 0 ; i < 10 && !earlyExit; i++) {
     for(int j = 0 ; i < 10 && !earlyExit; j++) { earlyExit = true; }
}

foreachループには適していません。
ジョンマクレーン

8

Java 8 Streamソリューション:

List<Type> types1 = ...
List<Type> types2 = ...

types1.stream()
      .flatMap(type1 -> types2.stream().map(type2 -> new Type[]{type1, type2}))
      .filter(types -> /**some condition**/)
      .findFirst()
      .ifPresent(types -> /**do something**/);

1
@ Tvde1でも他のユーザーにとっては有用なので、新しい方法やソリューションはいつでも歓迎されます
Andrei Suvorkov '16

うわー、それは醜いです。
DS。

7

通常、そのような場合、より意味のあるロジックの範囲に入ります。問題の反復された「for」オブジェクトの一部を検索または操作するとします。そのため、通常は機能的アプローチを使用します。

public Object searching(Object[] types) { // Or manipulating
    List<Object> typesReferences = new ArrayList<Object>();
    List<Object> typesReferences2 = new ArrayList<Object>();

    for (Object type : typesReferences) {
        Object o = getByCriterion(typesReferences2, type);
        if(o != null) return o;
    }
    return null;
}

private Object getByCriterion(List<Object> typesReferences2, Object criterion) {
    for (Object typeReference : typesReferences2) {
        if(typeReference.equals(criterion)) {
             // here comes other complex or specific logic || typeReference.equals(new Object())
             return typeReference;
        }
    }
    return null;
}

主な短所:

  • 約2行多い
  • 計算サイクルの消費量が多い、つまりアルゴリズムの観点から見ると遅い
  • よりタイピング作業

長所:

  • 機能の細分性により、懸念の分離に対する比率が高くなります
  • 再利用性と検索/操作ロジックの制御の比率が高い
  • 方法は長くないため、よりコンパクトで理解しやすい
  • 読みやすさの高い比率

したがって、別のアプローチでケースを処理するだけです。

基本的にこの質問の作者への質問:このアプローチについてあなたは何を考えますか?


6

label:およびフラグを使用せずに、すべてのループから抜けることができます。

トリッキーなソリューションです。

ここで、条件1はループKおよびJから抜け出すために使用される条件です。条件2はループK、JおよびIから抜けるために使用される条件です。

例えば:

public class BreakTesting {
    public static void main(String[] args) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                for (int k = 0; k < 9; k++) {
                    if (condition1) {
                        System.out.println("Breaking from Loop K and J");
                        k = 9;
                        j = 9;
                    }
                    if (condition2) {
                        System.out.println("Breaking from Loop K, J and I");
                        k = 9;
                        j = 9;
                        i = 9;
                    }
                }
            }
        }
        System.out.println("End of I , J , K");
    }
}

2
for-eachループでこれをどのように使用しますか?;)
ウィリーメンツェル2015

5
これは、list.size()> 5などのより高度なループ条件がある場合は機能しません。また、それは本当に単なるハックです。読みにくく、悪い習慣です!
Neuron、2015年

エラーが発生しやすいです。あなたが内部ループを変更想像し(int k = 0; k < 10; k++)て、あなたはすべての問題を解決しませんでしたk = 9k = 10。あなたは無限ループに入るかもしれません。
フロリアンF

5

ラベル付きブレークの概念は、Javaでネストされたループを分割するために使用されます。ラベル付きブレークを使用すると、任意の位置でループのネストを分割できます。例1:

loop1:
 for(int i= 0; i<6; i++){
    for(int j=0; j<5; j++){
          if(i==3)
            break loop1;
        }
    }

3つのループがあり、loop3を終了するとします。例2:

loop3: 
for(int i= 0; i<6; i++){
loop2:
  for(int k= 0; k<6; k++){
loop1:
    for(int j=0; j<5; j++){
          if(i==3)
            break loop3;
        }
    }
}

4
boolean broken = false; // declared outside of the loop for efficiency
for (Type type : types) {
    for (Type t : types2) {
        if (some condition) {
            broken = true;
            break;
        }
    }

    if (broken) {
        break;
    }
}

4

関数の内部にある場合は、単にそれを返さないのはなぜですか。

for (Type type : types) {
    for (Type t : types2) {
         if (some condition) {
            return value;
         }
    }
}

私はこのパターンを好む。ループを別の関数に分割することがよくありました。私のコードはそうした後もずっと良くなっているので、この理由で私はこの答えが本当に好きです。
ビルK

4

最良かつ簡単な方法

outerloop:
for(int i=0; i<10; i++){
    // here we can break Outer loop by 
    break outerloop;

    innerloop:
    for(int i=0; i<10; i++){
        // here we can break innerloop by 
        break innerloop;
     }
}

4
これらの分割の例は、ラベルがない場合でも同じ時点で分割されるため、あまり役に立ちません。また、それはinnerloopに到達することはできませんので、あなたが実際に、あなたのコードの場合ではありませんこれは、実行可能なコードを持っていることは常に良いことです...
ニューロン

同じことをタイプするつもりだった。この場合、ラベルは多少役に立たない。
Taslim Oseni

4

かなり珍しいアプローチですが、コード長(パフォーマンスではありません)に関しては、これが最も簡単な方法です。

for(int i = 0; i++; i < j) {
    if(wanna exit) {
        i = i + j; // if more nested, also add the 
                   // maximum value for the other loops
    }
}


4

例なしで言及された別の1つのソリューション(実際には製品コードで機能します)。

try {
    for (Type type : types) {
        for (Type t : types2) {
            if (some condition #1) {
                // Do something and break the loop.
                throw new BreakLoopException();
            }
        }
    }
}
catch (BreakLoopException e) {
    // Do something on look breaking.
}

もちろんBreakLoopException、内部、プライベート、およびスタックトレースなしで高速化する必要があります。

private static class BreakLoopException extends Exception {
    @Override
    public StackTraceElement[] getStackTrace() {
        return new StackTraceElement[0];
    }
}

1
それは実際に言及されており、回答で-23票を獲得しています... stackoverflow.com/a/886980/2516301。それは機能しますが、非常に悪いプログラミング習慣です...
vefthym

確かに。しかし、私はそのようなレガシーコードを見ました-4レベルのネストされたループで、いくつかの破壊条件があります。インラインコードではなく、例外を使用した方が読みやすくなりました。-23票は主に感情的な評価ですが、そうです-このアプローチは慎重に使用する必要があります。
ursa

1
センターリターンで別の関数呼び出しに分割されていれば、さらに読みやすくなります。多くの場合、小さなリファクタリングによってさらに改善されるため、スタンドアロンの(多くの場合再利用可能な)関数として理にかなっています。
ビルK

2

for (int j = 0; j < 5; j++) //inner loopに置き換える必要があり for (int j = 0; j < 5 && !exitloops; j++)ます。

ここで、この場合、条件がの場合、完全にネストされたループは終了するはずですTrue。でもexitloopsアッパーだけに使うと loop

 for (int i = 0; i < 5 && !exitloops; i++) //upper loop

その後、この内部ループに終了することを通知する追加のフラグがないため、内部ループが続行されます。

例:if i = 3j=2thenの条件はfalseです。しかし、内部ループの次の反復ではj=3 、条件(i*j)なっ9あるtrueもののまで内側のループが継続されますjなります5

そのexitloopsため、内部ループにも使用する必要があります。

boolean exitloops = false;
for (int i = 0; i < 5 && !exitloops; i++) { //here should exitloops as a Conditional Statement to get out from the loops if exitloops become true. 
    for (int j = 0; j < 5 && !exitloops; j++) { //here should also use exitloops as a Conditional Statement. 
        if (i * j > 6) {
            exitloops = true;
            System.out.println("Inner loop still Continues For i * j is => "+i*j);
            break;
        }
        System.out.println(i*j);
    }
}

2

@ 1800 INFORMATIONの提案のように、内側のループを中断する条件を外側のループの条件として使用します。

boolean hasAccess = false;
for (int i = 0; i < x && hasAccess == false; i++){
    for (int j = 0; j < y; j++){
        if (condition == true){
            hasAccess = true;
            break;
        }
    }
}

2

新しい実装の場合は、if-else_if-elseステートメントとしてロジックを書き換えてみてください。

while(keep_going) {

    if(keep_going && condition_one_holds) {
        // Code
    }
    if(keep_going && condition_two_holds) {
        // Code
    }
    if(keep_going && condition_three_holds) {
        // Code
    }
    if(keep_going && something_goes_really_bad) {
        keep_going=false;
    }
    if(keep_going && condition_four_holds) {
        // Code
    }
    if(keep_going && condition_five_holds) {
        // Code
    }
}

それ以外の場合は、特別な条件が発生したときにフラグを設定して、各ループ条件でそのフラグを確認できます。

something_bad_has_happened = false;
while(something is true && !something_bad_has_happened){
    // Code, things happen
    while(something else && !something_bad_has_happened){
        // Lots of code, things happens
        if(something happened){
            -> Then control should be returned ->
            something_bad_has_happened=true;
            continue;
        }
    }
    if(something_bad_has_happened) { // The things below will not be executed
        continue;
    }

    // Other things may happen here as well, but they will not be executed
    //  once control is returned from the inner cycle.
}

ここに!したがって、単純なブレークは機能しませんが、を使用して機能させることができますcontinue

ロジックを1つのプログラミング言語からJavaに移植するだけで、機能を動作させたい場合は、ラベルを使用してみてください。


2

デモのためにbreakcontinuelabel

Javaキーワードbreakcontinueデフォルト値があります。それは「最も近いループ」であり、今日、Javaを使用して数年後、私はそれを手に入れました!

まれに使用されているようですが、便利です。

import org.junit.Test;

/**
 * Created by cui on 17-5-4.
 */

public class BranchLabel {
    @Test
    public void test() {
        System.out.println("testBreak");
        testBreak();

        System.out.println("testBreakLabel");
        testBreakLabel();

        System.out.println("testContinue");
        testContinue();
        System.out.println("testContinueLabel");
        testContinueLabel();
    }

    /**
     testBreak
     a=0,b=0
     a=0,b=1
     a=1,b=0
     a=1,b=1
     a=2,b=0
     a=2,b=1
     a=3,b=0
     a=3,b=1
     a=4,b=0
     a=4,b=1
     */
    public void testBreak() {
        for (int a = 0; a < 5; a++) {
            for (int b = 0; b < 5; b++) {
                if (b == 2) {
                    break;
                }
                System.out.println("a=" + a + ",b=" + b);
            }
        }
    }

    /**
     testContinue
     a=0,b=0
     a=0,b=1
     a=0,b=3
     a=0,b=4
     a=1,b=0
     a=1,b=1
     a=1,b=3
     a=1,b=4
     a=2,b=0
     a=2,b=1
     a=2,b=3
     a=2,b=4
     a=3,b=0
     a=3,b=1
     a=3,b=3
     a=3,b=4
     a=4,b=0
     a=4,b=1
     a=4,b=3
     a=4,b=4
     */
    public void testContinue() {
        for (int a = 0; a < 5; a++) {
            for (int b = 0; b < 5; b++) {
                if (b == 2) {
                    continue;
                }
                System.out.println("a=" + a + ",b=" + b);
            }
        }
    }

    /**
     testBreakLabel
     a=0,b=0,c=0
     a=0,b=0,c=1
     * */
    public void testBreakLabel() {
        anyName:
        for (int a = 0; a < 5; a++) {
            for (int b = 0; b < 5; b++) {
                for (int c = 0; c < 5; c++) {
                    if (c == 2) {
                        break anyName;
                    }
                    System.out.println("a=" + a + ",b=" + b + ",c=" + c);
                }
            }
        }
    }

    /**
     testContinueLabel
     a=0,b=0,c=0
     a=0,b=0,c=1
     a=1,b=0,c=0
     a=1,b=0,c=1
     a=2,b=0,c=0
     a=2,b=0,c=1
     a=3,b=0,c=0
     a=3,b=0,c=1
     a=4,b=0,c=0
     a=4,b=0,c=1
     */
    public void testContinueLabel() {
        anyName:
        for (int a = 0; a < 5; a++) {
            for (int b = 0; b < 5; b++) {
                for (int c = 0; c < 5; c++) {
                    if (c == 2) {
                        continue anyName;
                    }
                    System.out.println("a=" + a + ",b=" + b + ",c=" + c);
                }
            }
        }
    }
}

2

デモ

public static void main(String[] args) {
    outer:
    while (true) {
        while (true) {
            break outer;
        }
    }
}

1

次のことができます。

  1. ローカル変数を false

  2. trueブレークしたいときに、最初のループでその変数を設定します

  3. 次に、外側のループをチェックインして、条件が設定されているかどうかを確認し、外側のループから抜けることもできます。

    boolean isBreakNeeded = false;
    for (int i = 0; i < some.length; i++) {
        for (int j = 0; j < some.lengthasWell; j++) {
            //want to set variable if (){
            isBreakNeeded = true;
            break;
        }
    
        if (isBreakNeeded) {
            break; //will make you break from the outer loop as well
        }
    }

1

whileここでは、ループを効果的に使用できる場合があります。

Random rand = new Random();
// Just an example
for (int k = 0; k < 10; ++k) {
    int count = 0;
    while (!(rand.nextInt(200) == 100)) {
       count++;
    }

    results[k] = count;
}

1

Javaには、C ++にあるようなgoto機能がありません。しかし、それでも、gotoJavaの予約キーワードです。彼らは将来それを実装するかもしれません。あなたの質問の答えは、Javaにはlabelと呼ばれるものがあり、continueand breakステートメントを適用できるということです。以下のコードを見つけてください:

public static void main(String ...args) {
    outerLoop: for(int i=0;i<10;i++) {
    for(int j=10;j>0;j--) {
        System.out.println(i+" "+j);
        if(i==j) {
            System.out.println("Condition Fulfilled");
            break outerLoop;
        }
    }
    }
    System.out.println("Got out of the outer loop");
}

1

外側のループのフラグを作成し、内側のループを実行するたびに答えを確認することもできます。

このような:

for (Type type : types) {
    boolean flag=false;
    for (Type t : types2) {
        if (some condition) {
            // Do something and break...
            flag=true;
            break; // Breaks out of the inner loop
        }
    }
    if(flag)
        break;
}

1
boolean condition = false;
for (Type type : types) {
    for (int i = 0; i < otherTypes.size && !condition; i ++) {
        condition = true; // If your condition is satisfied
    }
}

condition処理が完了したときのフラグとして使用します。その後、条件が満たされていない間だけ内側のループが継続します。どちらの方法でも、外側のループは継続します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.