题 在Java中打破嵌套循环


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

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

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

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

更新:当我完成循环块的执行时,我不想重新运行循环。


1542
2018-05-20 09:07


起源




答案:


像其他回答者一样,我肯定 比较喜欢 将内循环放在不同的方法中。这个答案只是说明了如何满足问题中的要求。

您可以使用 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

2084
2018-05-20 09:11



这个 不 循环后直接跳转到。尝试一下!是的,标签出现在循环之前,但这是因为它标记了循环,而不是您要退出的位置。 (你也可以继续使用标签。) - Jon Skeet
Perl也允许这个自己的标签系统。我认为很多语言都会这样做 - 我觉得它在Java中并不令我惊讶。 - Evan Carroll
@Evan - 这种说法显然是正确的 - 用承诺这是真的语言。但Java没有做出这样的承诺。如果某种语言与您的假设相冲突,那么您的假设可能是错误的。在这种情况下,我认为你仍然部分正确 - 最少惊喜的原则WRT许多从未听说过(或忘记)那种形式的人 break。即便如此,例外是另一个更为人所知的例外(抱歉)。但是如果不明显的话,我仍然会对此感到不满(小循环,警告评论,如果标签/破坏仍然不够明显)。 - Steve314
@JWiley。同样在这里;更容易理解:当你“击中”返回时,你可以停止阅读 - 更容易维护:在返回之前修复某些东西时,你不必担心循环之外的副作用。恕我直言,快速返回通常会导致更紧凑,更简单的代码。 - Bruno Grieder
@MuhammadBabar: outerloop 是一个标签。我不知道你尝试了什么代码,但我的答案中的代码编译并运行得很好。 - Jon Skeet


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

这样可以获得可读性。

代码会变成这样的:

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

369
2017-11-14 18:26



有时你使用几个局部变量在内部循环之外,将它们全部传递都可以感觉很笨重。 - Haoest
那么这个解决方案应该如何在接受的答案中打印“完成”? - JohnDoe
@John你调用它然后你打印System.out.println(“完成”);在搜索方法中尝试{} finally {}也是一个选项。 - Zo72
我猜这是更好的做法,但是如果你想继续而不是破坏会发生什么?标签支持同样好(或严重!)但我不知道如何将此逻辑转换为继续。 - Robert Grant
@RobertGrant如果你想继续而不是休息,移动外部循环 loop 方法并从方法返回继续。 - Muhd


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

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

186
2018-05-20 09:12



您无需创建新块即可使用标签。 - Jon Skeet
不,但它使意图更加清晰。查看已接受答案的第一条评论。 - Bombe
它实际上不是一个命名块,在标签之后你可以将任何Java表达式写成没有标签的那样 name: if(...){...} 提出命名条件? :) - La VloZ Merrill


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

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

112
2017-12-07 17:52



不应该这样 && !finished 代替 || !finished?为什么然后使用 break 根本不使用 && !finished 对于内循环呢? - Gandalf
我用 break 能够任意退出循环。如果之后有代码 if 块,你可以 break 在它执行之前。但你是对的 &&。修复。 - Elle Mundy
好方案!如果把它放在一个额外的功能中,那就是我在实践中如何做到这一点因某些原因不可取。 - benroth
如果在内部循环之后存在某些逻辑,则会存在一个潜在的问题...这将继续执行,并且外部循环仅在新迭代开始时中断... - Karthik Karuppannan
我不明白为什么要这样做。使用代码的人应该能够使用语言的所有功能。我认为编写其他人可以理解的代码非常重要,但不能限制使用语言提供的官方工具并找到相同功能的变通方法。或者你的意思是“坏习惯”吗? - codepleb


你可以使用标签:

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

79
2018-05-20 09:11



不公平,这个答案也是正确的。但只有55个赞成票.. - WesternGun
阅读也简单得多。 - dirtysocks45


也许有功能?

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

32
2018-05-20 11:43





您可以使用临时变量:

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

15
2018-05-20 09:11



我发现这种方式很混乱。 - boutta
笨拙且容易出错恕我直言。 - orbfish
每次循环都要检查额外的条件?不用了,谢谢。 - ryandenki


如果你不喜欢 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;
        }
    }
}

10
2017-12-21 22:56





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

9
2018-02-05 06:04



在条件破坏后迭代所有项目我觉得很酷。因此,我会在else案例中添加一个休息时间。 - boutta
@boutta我不确定你是如何得出这个结论的。一旦条件为真,则退出两个循环。 - Swifty McSwifterton
好吧,我没有得到操纵's'var的部分。但我发现那种不好的风格,因为s代表了大小。然后我更喜欢ddyer的答案和明确的变量: stackoverflow.com/a/25124317/15108 - boutta
@boutta你可以改变 s 到一个低于的值 i 或改变 i大于或等于的值 s,两者都应该做到这一点。你改变是对的 s,因为它可以在以后的其他地方使用,但会改变 i 不会伤害,只会确保第一个 for 不会继续循环。 - Zsolti


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

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

5
2017-08-04 17:29





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**/);

5
2017-07-16 10:24