如何打破 Java 中的嵌套循环?

How do I break out of nested loops in Java?

提问人:boutta 提问时间:5/20/2009 最后编辑:Alireza Nooraliboutta 更新时间:4/9/2023 访问量:1405311

问:

我有一个嵌套的循环结构,如下所示:

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

现在我怎样才能打破这两个循环?我也研究过类似的问题,但没有一个是专门针对 Java 的。我无法应用这些解决方案,因为大多数人都使用 gotos。

我不想把内部循环放在不同的方法中。

我不想返回循环。当中断时,我完成了循环块的执行。

Java 套循环

评论


答:

2731赞 Jon Skeet 5/20/2009 #1

像其他回答者一样,我肯定更愿意将循环放在不同的方法中,此时您可以返回以完全停止迭代。这个答案只是显示了如何满足问题中的要求。

您可以与外环的标签一起使用。例如: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

评论

315赞 Jon Skeet 5/20/2009
确实在循环之后直接跳转到。试试吧!是的,标签在循环之前,但那是因为它标记了循环,而不是你想退出的地方。(您也可以继续使用标签。
0赞 Graeme Gill 9/12/2022
只有当状态很小,或者以可以通过引用传递的形式保存时,将其分解为不同的方法才是一种胜利。如果循环所引用/操作的状态较大,那么就必须采用一种优雅的方式来打破/继续循环。
2赞 iCantC 10/31/2022
直到这个答案打动了我,我从来不知道 Java 有标记循环的概念 ̄_(ツ)_/ ̄
127赞 simon622 5/20/2009 #2

您可以使用标签:

label1: 
for (int i = 0;;) {
    for (int g = 0;;) {
      break label1;
    }
}
24赞 Miguel Ping 5/20/2009 #3

您可以使用临时变量:

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;
         }
    }
}
240赞 Joey 5/20/2009 #4

您可以在循环周围使用命名块:

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

评论

44赞 Jon Skeet 5/20/2009
您无需创建新块即可使用标签。
94赞 Bombe 5/20/2009
不,但它使意图更加清晰。请参阅已接受答案的第一条评论。
44赞 Fortega 5/20/2009 #5

使用函数:

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;
      }
    }
  }
}
437赞 Zo72 11/15/2011 #6

从技术上讲,正确的答案是标记外环。在实践中,如果你想在内部循环内的任何一点退出,那么你最好将代码外部化到一个方法(如果需要的话,是一个静态方法),然后调用它。

这将为可读性带来回报。

代码将变成这样:

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);
            }
        }
    }
}

评论

35赞 Haoest 12/7/2011
有时,您会在内部循环之外使用多个局部变量,将它们全部传入可能会感觉很笨拙。
154赞 Elle Mundy 12/8/2011 #7

我从不使用标签。这似乎是一种不好的做法。这是我会做的:

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;
        }
    }
}

通过标志变量,我们可以很容易地控制任意数量的循环的执行,只是我们需要在循环条件语句中保持这个标志值检查,就像在 main loop->i < 5 && !finished 中所做的那样。

评论

1赞 ManBehindTheCurtain 9/13/2023
所以你允许额外的循环,这样你就不必标记“for”了吗?
13赞 Swifty McSwifterton 2/5/2012 #8

我需要做类似的事情,但我选择不使用增强的 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;
        }
    }
}

评论

0赞 boutta 1/29/2016
我不觉得在条件被破坏后迭代所有项目很酷。因此,我会在 else 情况下添加一个中断。
0赞 Swifty McSwifterton 8/1/2016
@boutta 我不确定你是如何得出这个结论的。条件为真后,两个循环都将退出。
0赞 boutta 11/10/2016
好的,我没有通过操作“s”var 来获得部分。但是我发现这种风格很糟糕,因为 s 代表大小。那么我更喜欢 ddyer 的答案,带有显式变量:stackoverflow.com/a/25124317/15108
0赞 Zsolti 9/27/2017
@boutta 您可以更改为低于或更改为大于或等于的值,两者都可以解决问题。你对 change 是对的,因为它以后可以在其他地方使用,但改变不会有什么坏处,只是会确保第一个不会继续循环。siissifor
1赞 Mahesh Peri 10/17/2012 #9

你只需使用标签来打破内部循环

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");
}
}
5赞 Panzercrisis 9/12/2013 #10
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;
    }
}
17赞 zord 12/22/2013 #11

如果你不喜欢 s 和 s,你可以使用“传统”的 for 循环来代替 for-in,并有一个额外的中止条件:breakgoto

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;
        }
    }
}

评论

3赞 John McClane 12/5/2018
不适合 foreach 循环。
3赞 Quintec 12/7/2018
@JohnMcClane 你在一个 9+ 岁问题的许多答案上都发垃圾邮件,因为......?
8赞 Hitendra Solanki 5/21/2014 #12

您可以在不使用任何 label: 和 flags 的情况下中断所有循环。

这只是一个棘手的解决方案。

这里的 condition1 是用于中断循环 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赞 Willi Mentzel 7/4/2015
我将如何将其与 for-each 循环一起使用?;)
5赞 Neuron 10/26/2015
如果您有更复杂的循环条件,例如 list.size()>5,则不起作用。而且它实际上只是一个黑客。很难阅读,而且做法不好!
0赞 Florian F 8/26/2018
它很容易出错。想象一下,您将内部循环更改为 ,但您没有修复所有 to 。你可能会陷入无限循环。(int k = 0; k < 10; k++)k = 9k = 10
6赞 Rumesh Eranga Hapuarachchi 6/1/2014 #13

使用标签。

INNER:for(int j = 0; j < numbers.length; j++) {
    System.out.println("Even number: " + i + ", break  from INNER label");
    break INNER;
}

请参阅此文章

评论

0赞 Gothiquo 1/23/2023
链接不起作用
11赞 ddyer 8/5/2014 #14

我更喜欢在循环测试中添加一个明确的“退出”。它清楚地表明 任何可能提前终止循环的普通读者。

boolean earlyExit = false;
for(int i = 0 ; i < 10 && !earlyExit; i++) {
     for(int j = 0 ; i < 10 && !earlyExit; j++) { earlyExit = true; }
}
2赞 mtyson 9/14/2014 #15

像@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;
        }
    }
}
5赞 ursa 10/10/2014 #16

另一个解决方案,在没有示例的情况下提到(它实际上适用于生产代码)。

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.
}

当然,应该是内部的、私有的,并使用 no-stack-trace 加速:BreakLoopException

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

评论

2赞 vefthym 10/10/2014
实际上已经提到过,在获得 -23 票的答案中......stackoverflow.com/a/886980/2516301 .它会完成工作,但这是一个非常糟糕的编程实践......
0赞 ursa 10/10/2014
事实上。但是我看到了这样的遗留代码 - 具有多个中断条件的 4 级嵌套循环。而且它比内联代码更具可读性,但有例外。-23 票主要是情绪评级,但是是的 - 这种方法应该谨慎使用。
1赞 Bill K 12/2/2017
如果它被分解成一个单独的函数调用,并带有中心返回,它会更具可读性。通常通过一些重构变得更好,因此它作为一个独立的(通常是可重用的)函数是有意义的。
5赞 user2875404 4/26/2015 #17

相当不寻常的方法,但就代码长度(而不是性能)而言,这是您可以做的最简单的事情:

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
    }
}
5赞 Keshav bansal 8/24/2015 #18

最好和最简单的方法..

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;
     }
}

评论

6赞 Neuron 10/26/2015
恕我直言,这些中断的例子不是很有帮助,因为即使没有标签,它们也会在同一点中断。此外,拥有可以实际执行的代码总是很好的,而您的代码并非如此,因为永远无法到达内部循环。
0赞 Taslim Oseni 1/12/2018
我本来打算输入同样的东西。在这种情况下,标签有些无用。
9赞 Oleksii Kyslytsyn 1/29/2016 #19

通常在这种情况下,它属于更有意义的逻辑范围,比如说对一些迭代的“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;
}

主要缺点:

  • 大约两倍以上的行
  • 计算周期消耗更多,这意味着从算法的角度来看,它的速度更慢
  • 更多打字工作

优点:

  • 由于功能粒度,关注点分离的比率更高
  • 可重用性和可控性比率更高 搜索/操作逻辑,无需
  • 方法不长,因此更紧凑,更容易理解
  • 更高的可读性

所以它只是通过不同的方法处理案件。

基本上是这个问题的作者的问题:您如何看待这种方法?

3赞 Vikrant Kashyap 4/8/2016 #20

for (int j = 0; j < 5; j++) //inner loop应替换为 。for (int j = 0; j < 5 && !exitloops; j++)

在这里,在这种情况下,如果条件是 ,则完整的嵌套循环应该退出。但是如果我们只使用鞋面Trueexitloopsloop

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

然后内部循环将继续,因为没有额外的标志通知这个内部循环退出。

示例:if 和 then 条件为 。但是在内循环的下一次迭代中,条件变为 which is 但内循环将继续直到 become 。i = 3j=2falsej=3(i*j)9truej5

因此,它也必须用于内部循环。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);
    }
}
5赞 Chit Khine 8/25/2016 #21

如果它在某个函数中,为什么不直接返回它:

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

评论

0赞 Bill K 12/2/2017
我更喜欢这种模式。它经常导致我将循环分解为一个单独的函数。这样做后,我的代码总是更好,所以出于这个原因,我真的很喜欢这个答案。
2赞 Ravindra HV 4/2/2017 #22

如果是新的实现,可以尝试将逻辑重写为 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

如果你只是将逻辑从一种编程语言移植到 Java 上,并且只想让它正常工作,你可以尝试使用标签

4赞 Bill 5/4/2017 #23

演示 、 和 :breakcontinuelabel

Java 关键字,并具有默认值。这是“最近的循环”,今天,在使用 Java 几年后,我刚刚掌握了它!breakcontinue

它似乎很少使用,但很有用。

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);
                }
            }
        }
    }
}
10赞 Ihor Rybak 7/16/2017 #24

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赞 Andrei Suvorkov 4/16/2019
@Tvde1并且对其他用户仍然有用,因此始终欢迎新的方法和解决方案
1赞 Siddharth Choudhary 9/19/2017 #25

您可以执行以下操作:

  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
        }
    }
    
2赞 Harsh Vardhan 8/4/2018 #26

Java 没有像 C++ 那样的 goto 功能。但是,仍然是 Java 中的保留关键字。他们可能会在未来实施它。对于您的问题,答案是 Java 中有一种叫做 label 的东西,您可以将 and 语句应用于它。找到下面的代码:gotocontinuebreak

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");
}
10赞 Bishal Jaiswal 4/4/2019 #27

标记的中断概念用于在 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;
        }
    }
}
9赞 duyuanchao 8/26/2019 #28

演示

public static void main(String[] args) {
    outer:
    while (true) {
        while (true) {
            break outer;
        }
    }
}
6赞 Kiran Maniya 9/20/2019 #29

使用起来相当简单,可以使用标签将外循环从内部循环中分解开来,考虑下面的例子,label

public class Breaking{
    public static void main(String[] args) {
        outerscope:
        for (int i=0; i < 5; i++) {
            for (int j=0; j < 5; j++) {
                if (condition) {
                    break outerscope;
                }
            }
        }
    }
}

另一种方法是使用中断变量/标志来跟踪所需的中断。请看以下示例。

public class Breaking{ 
    public static void main(String[] args) {
        boolean isBreaking = false;
        for (int i=0; i < 5; i++) {
            for (int j=0; j < 5; j++) {
                if (condition) {
                    isBreaking = true;
                    break;
                }
            }
            if(isBreaking){
                break;
            }
        }
    }
}

但是,我更喜欢使用第一种方法。

21赞 RusJaI 10/17/2020 #30

当您需要退出多个循环时,单独使用“break”关键字不是合适的方法。 您可以退出即时循环 不管你的语句被包围了多少个循环。 您可以将“break”与标签一起使用! 在这里,我使用了标签“abc” 您可以在 Java 中的任何函数中编写代码

此代码演示如何从最外层循环退出

 abc: 
    for (int i = 0; i < 10; i++) { 
        for (int j = 0; j < 10; j++) { 
           for (int k = 0; k < 10; k++) { 
              if (k == 1){
                 break abc;
              } 
        } 
    } 
}

此外,还可以使用 break 语句从嵌套循环中的任何循环中退出。

    for (int i = 0; i < 10; i++) { 
       abc:for (int j = 0; j < 10; j++) { 
           for (int k = 0; k < 10; k++) { 
              if (k == 1){
                 break abc;
              } 
        } 
    } 
}

以下代码显示了从最内层循环退出的示例。 在其他作品中,执行以下代码后,你处于“k”变量循环的外部,仍然在“j”和“i”变量的循环内。

    for (int i = 0; i < 10; i++) { 
        for (int j = 0; j < 10; j++) { 
           for (int k = 0; k < 10; k++) { 
              if (k == 1){
                 break;
              } 
        } 
    } 
}