如何突破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特有的。我无法应用这些解决方案,因为大多数使用的gotos。

我不想将内部循环使用其他方法。

我不想重新运行循环。中断时,我完成了循环块的执行。

Answers:


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
确实在循环之后直接跳到。尝试一下!是的,标签位于循环之前,但这是因为它在标记循环,而不是您要退出的位置。(您也可以继续添加标签。)
乔恩·斯基特

2
Perl还允许其自己的标签系统。我认为许多语言都可以使用它-Java并不奇怪。
埃文·卡罗尔

9
@Evan-这个声明显然是正确的-在保证它是正确的语言中。但是Java没有实现这一承诺。如果一种语言与您的假设相冲突,则很可能是您的假设有误。在这种情况下,我认为您仍然是部分正确的原则-对于许多从未听说过(或忘记了)这种形式的的人,可以给WRT带来最少的惊喜break。即使这样,异常还是另一个众所周知的异常(对不起)。但是,如果它不是很明显,我仍然会对此感到不满意(小循环,如果标签/中断仍然不够明显,则会发出警告注释)。
Steve314 2011年

6
@MuhammadBabar:outerloop是一个标签。我不知道您到底尝试了什么代码,但是答案中的代码可以编译并正常运行。
乔恩·斯基特

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 2011年

1
那么,该解决方案应该如何像接受的答案一样打印“完成”?
JohnDoe

1
@JohnDoe调用它,然后打印System.out.println(“ done”); 在搜索方法中尝试{}最终{}也是一种选择。
Zo72 2012年

2
我猜这是更好的做法,但是如果您要继续而不是中断会怎样?标签支持同样好(或坏!),但是我不确定如何继续转换此逻辑。
Rob Grant

@RobertGrant如果要继续而不是中断,请将外部循环移到loop方法之外,然后从方法返回以继续。
Muhd

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直接标记相比,此构造具有很大的优势。}仅当从未满足条件时,才可以在将要执行的最后一个代码之前添加代码。
Florian F

2
它是一个命名块,而不是一个命名循环。您无法在该循环中“继续搜索”;如果将循环命名为search,则这完全是合法的语法。您可以破坏它,但不能继续。

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内部循环呢?
甘道夫

4
我曾经break能够任意退出循环。如果该代码if块之后有代码,则可以break在代码执行之前。但是你是对的&&。固定它。
Elle Mundy

2
不错的解决方案!如果出于某种原因不希望将其放在额外的函数中,那正是我在实践中的做法。
benroth

6
如果内部循环之后存在某些逻辑,则可能会出现问题...该逻辑将继续执行,并且仅当新迭代开始时才中断外部循环...
Karthik Karuppannan

7
我不明白为什么要那样做。使用代码的人应该能够使用语言的所有功能。我知道,编写其他人可以理解的代码很重要,但不能通过限制语言所提供的官方工具的使用以及找到相同功能的变通办法来解决。还是您说的“不良做法”是什么意思?
codepleb

107

您可以使用标签:

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

4
向我+1。简单地说,可以回答问题。不能因为重复您的回答而被指控重复现有的回答。
Heimdall

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
我发现这种方式比较混乱。
09年

11
每次通过循环进行额外的条件检查?不用了,谢谢。
ryandenki 2011年

15

如果您不喜欢breaks和gotos,则可以使用“传统” for循环而不是for-in,并带有额外的中止条件:

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

1
@JohnMcClane您在一个9岁以上的问题上对许多答案都发了垃圾邮件,因为...?
Quintec '18

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

条件破损后,我认为迭代所有项目并不酷。因此,在其他情况下我要加个休息时间。
7:58 boutta

@boutta我不确定您如何得出这个结论。条件为真后,两个循环都退出。
Swifty McSwifterton,2016年

好吧,我没有参与's'var的操作。但是我发现这种不好的风格,因为s代表大小。然后,我更喜欢从ddyer使用显式变量的答案:stackoverflow.com/a/25124317/15108
boutta

@boutta您可以更改s为小于的值,i或者更改i为大于或等于的值s,两者都可以解决问题。您对更改是正确的s,因为它可以在以后的其他地方使用,但是更改i不会造成危害,只需确保第一个for不会继续循环即可。
Zsolti

9

我更喜欢在循环测试中添加一个明确的“退出”。任何随便的读者都可以清楚知道循环可能会提前终止。

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

哇,真丑。
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;
}

主要缺点:

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

优点:

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

因此,它只是通过不同的方法来处理案件。

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


6

您可以不使用任何label:和标志而中断所有循环。

这只是棘手的解决方案。

这里的condition1是用于中断循环K和J的条件。而condition2是用于中断循环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循环?;)
Willi Mentzel

5
如果您有更复杂的循环条件(例如list.size()> 5),则此方法不起作用。而且它实际上只是一个hack。很难阅读且不好实践!
Neuron

这很容易出错。想象一下,您将内部循环更改为,(int k = 0; k < 10; k++)而没有将所有固定k = 9k = 10。您可能会陷入无限循环。
Florian 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;
         }
    }
}

我喜欢这种模式。它常常使我将循环分解为一个单独的函数。这样做之后,我的代码始终会更好,因此,由于这个原因,我真的很喜欢这个答案。
Bill 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
这些破坏的例子并不是很有帮助,因为即使没有标签,它们也会在同一点断裂。另外,拥有可以实际执行的代码总是很不错的,这对您的代码来说不是这种情况,因为永远无法到达内部循环。。–
Neuron

我要输入相同的内容。在这种情况下,标签有些用处。
塔斯林·奥塞尼

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

另一个解决方案,没有示例提及(实际上在产品代码中有效)。

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 2014年

确实。但是我看到了这样的遗留代码-具有多个中断条件的4级嵌套循环。而且它比带有内联代码的异常更具可读性。-23票主要是对情感的评价,但是可以-这种方法应谨慎使用。
ursa 2014年

1
如果将它分解成一个带有中心返回的单独函数调用,它将更加可读。通常通过一点重构就可以使它变得更好,因此它可以作为独立(经常可重用)的功能使用。
Bill K

2

for (int j = 0; j < 5; j++) //inner loop应该用代替 for (int j = 0; j < 5 && !exitloops; j++)

在这种情况下,如果condition为,则应退出完整的嵌套循环True。但是如果我们exitloops只用上 loop

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

然后,内部循环将继续,因为没有多余的标志通知该内部循环退出。

例如:如果i = 3j=2然后条件false。但在内部循环的下一个迭代j=3 ,然后条件(i*j)成为9true可是内部循环将持续到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

如果您只是将逻辑从一种编程语言移植到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功能。但仍然goto是Java中的保留关键字。他们可能会在将来实施它。对于您的问题,答案是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为标志当您完成处理。然后,内部循环仅在不满足条件的情况下继续进行。无论哪种方式,外部循环都将持续不断。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.