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

bou*_*tta 1751 java loops

我有一个这样的嵌套循环结构:

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

现在我怎样才能摆脱两个循环?我看过类似的问题,但没有一个特别关注Java.我无法应用这些解决方案,因为大多数使用了gotos.

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

我不想重新运行循环.当我完成循环块的执行时.

Jon*_*eet 2333

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

您可以使用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");
    }
}
Run Code Online (Sandbox Code Playgroud)

这打印:

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
Run Code Online (Sandbox Code Playgroud)

  • 这个*会在循环后直接跳转到.试试吧!是的,标签出现在循环之前,但这是因为它标记了循环,而不是您要退出的位置.(您也可以继续使用标签.) (277认同)
  • @Evan - 这种说法显然是正确的 - 用承诺这是真的语言.但Java没有做出这样的承诺.如果某种语言与您的假设相冲突,那么您的假设可能是错误的.在这种情况下,我认为你仍然部分正确 - 最少惊喜的原则WRT许多从未听说过(或忘记)那种形式的"休息"的人.即便如此,例外是另一个更为人所知的例外(对不起).但是如果不明显的话,我仍然会对此感到不满(小循环,警告评论,如果标签/休息仍然不够明显). (9认同)
  • @MuhammadBabar:`outerloop`是一个标签.我不知道你尝试了什么代码,但我的答案中的代码编译并运行得很好. (5认同)
  • @NisargPatil:推理似乎是合理的,尽管我认为我不会将其称为“主要代码气味”。不过,根据我回答的开头,我更愿意将它放在一个单独的方法中。 (3认同)
  • 直到这个答案击中我之前,我从来不知道 Java 有标记循环的概念 ́\_(ツ)_/̅ (3认同)
  • Perl也允许这个自己的标签系统.我认为有很多语言可以做到这一点 - 我不会惊讶于它是用Java编写的. (2认同)
  • @JWiley.同样在这里; 更容易阅读:当你"击中"返回时,你可以停止阅读 - 更容易维护:在返回之前修复某些东西时,你不必担心循环之外的副作用.恕我直言,快速返回通常会导致更紧凑,更简单的代码. (2认同)
  • @NisargPatil仅仅因为它在sonarLint中就没有使其成为代码的味道。这只是意味着有一个开发人员将其添加到sonarLint中,并带有一点痒的痒感,并且充满了这些规则,这些规则只有在被滥用时才有意义,或者因为某些开发人员对它们有个人仇恨的意图。带标签的中断和继续是描述您要做什么的一种非常优雅的方法。 (2认同)

Zo7*_*o72 393

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

这样可以获得可读性.

代码会变成这样:

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

匹配接受答案的示例:

 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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 有时你使用几个局部变量在内部循环之外,将它们全部传递进去可以感觉很笨重. (24认同)
  • 我猜这是更好的做法,但是如果你想继续而不是破坏会发生什么?标签支持同样好(或严重!)但我不知道如何将此逻辑转换为继续. (2认同)

Joe*_*oey 211

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

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

  • 不,但它使意图更加清晰.查看接受答案的第一条评论. (78认同)
  • 您无需创建新块即可使用标签. (36认同)
  • 这种结构比直接标记`for`有很大的优势.您可以在最后一个`}之前添加代码,只有在从未满足条件时才会执行. (3认同)
  • 它实际上不是一个命名块,在标签之后你可以把任何Java表达式写成没有标签的那样,`name:if(...){...}`使命名条件?:) (2认同)
  • 它是一个命名块,而不是一个命名循环。您无法在该循环中“继续搜索”;如果将循环命名为search,则这完全是合法的语法。您可以破坏它,但不能继续。 (2认同)

Ell*_*ndy 128

我从不使用标签.进入这似乎是一种不好的做法.这就是我要做的事情:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我不明白为什么要这样做.使用代码的人应该能够使用语言的所有功能.我认为编写其他人可以理解的代码很重要,但不能限制使用语言提供的官方工具并找到相同功能的变通方法.或者你的意思是"坏习惯"吗? (7认同)
  • 如果在内部循环之后存在某些逻辑,那么可能存在一个问题...这将继续执行,并且外部循环仅在新迭代开始时中断... (6认同)
  • 不应该是`&&!finished`而不是`|| !finished`?那么为什么然后使用`break`而不是使用`&&!finished`作为内循环呢? (4认同)
  • 我使用`break`来任意退出循环.如果在`if`块之后有代码,你可以在执行之前`break`.但你对'&&'是正确的.固定它. (4认同)
  • 好方案!如果把它放在一个额外的功能中,那就是我在实践中如何做到这一点并不是出于某些原因. (2认同)

sim*_*622 102

你可以使用标签:

label1: 
for (int i = 0;;) {
    for (int g = 0;;) {
      break label1;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 得到我的 +1。简单,切中要害,回答了这个问题。不能因为您同时回答而被指责重复现有答案。 (4认同)

For*_*ega 38

使用功能:

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;
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)


Rus*_*JaI 22

当您需要退出多个循环时,单独使用“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;
              } 
        } 
    } 
}
Run Code Online (Sandbox Code Playgroud)

您还可以使用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;
              } 
        } 
    } 
}
Run Code Online (Sandbox Code Playgroud)

以下代码显示了从最内层循环退出的示例。在其他作品中,执行以下代码后,您位于“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;
              } 
        } 
    } 
}
Run Code Online (Sandbox Code Playgroud)


Mig*_*ing 18

您可以使用临时变量:

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
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

根据您的功能,您也可以从内循环退出/返回:

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

  • 每次循环都要检查额外的条件?不用了,谢谢. (9认同)
  • 我发现这种方式很混乱. (7认同)

zor*_*ord 13

如果您不喜欢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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 不适合 foreach 循环。 (3认同)
  • @JohnMcClane 而且你在一个 9 岁以上的问题的许多答案上都发垃圾邮件,因为......? (3认同)

Swi*_*ton 11

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


ddy*_*yer 9

我更喜欢在循环测试中添加一个明确的"退出".任何随意的读者都清楚地知道循环可以提前终止.

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


Bis*_*wal 9

标记中断概念用于在 Java 中中断嵌套循环,通过使用标记中断,您可以在任何位置中断循环嵌套。示例 1:

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

假设有 3 个循环并且您想终止循环 3:示例 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Igo*_*bak 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**/);
Run Code Online (Sandbox Code Playgroud)


Rum*_*chi 7

使用标签。

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

参考这篇文章


Hit*_*nki 6

您可以在不使用任何标签的情况下从所有循环中断:和标志.

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

这里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");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您有一个更复杂的循环条件,例如list.size()> 5,则不起作用.它真的只是一个黑客.它很难阅读和不好的练习! (5认同)
  • 我如何使用for-each循环?;) (2认同)

小智 6

最好和最简单的方法..

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;
     }
}
Run Code Online (Sandbox Code Playgroud)

  • 这些打破的例子恕我直言不是很有帮助,因为即使没有标签,他们也会在同一点打破。此外,拥有您可以实际执行的代码总是很好,而您的代码并非如此,因为永远无法到达内循环。 (4认同)

Ole*_*syn 6

通常在这种情况下,它会进入更有意义的逻辑范围,让我们说一些搜索或操纵一些迭代的"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;
}
Run Code Online (Sandbox Code Playgroud)

主要缺点:

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

专业人士:

  • 由于功能粒度,关注点分离的比例越高
  • 更高的可重用性比率和没有的搜索/操纵逻辑的控制
  • 方法不长,因此它们更紧凑,更容易理解
  • 主观上可读性的比率更高

所以它只是通过不同的方法处理案例.

对这个问题的作者来说基本上是一个问题:你对这种方法有什么看法?


she*_*hub 6

演示

public static void main(String[] args) {
    outer:
    while (true) {
        while (true) {
            break outer;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Kir*_*iya 6

它相当容易使用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;
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用中断变量/标志来跟踪所需的中断。考虑下面的例子。

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Pan*_*sis 5

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;
    }
}
Run Code Online (Sandbox Code Playgroud)


urs*_*rsa 5

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

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.
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 事实上,在获得-23票的答案中已经提到过......http://stackoverflow.com/a/886980/2516301。它可以完成工作,但这是一种非常糟糕的编程习惯...... (2认同)

use*_*404 5

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)


Chi*_*ine 5

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

for (Type type : types) {
    for (Type t : types2) {
         if (some condition) {
            return value;
         }
    }
}
Run Code Online (Sandbox Code Playgroud)