在 Java 中使用 break 退出循环是不是不好的做法?

我想知道使用 break语句退出循环而不实现循环条件是否是一种“糟糕的做法”?

我在 Java 和 JVM 方面没有足够的洞察力来了解循环是如何处理的,所以我想知道我这样做是否忽略了一些关键的东西。

这个问题的焦点是: 是否存在特定的性能开销?

119219 次浏览

不,这不是一个坏的做法。这是最简单和有效的方法。

当然没有。有时候,在不满足逻辑循环条件的情况下,可能会在循环中发生满足总体需求的事情。在这种情况下,使用 break来阻止您无意义地在循环中循环。

例子

String item;


for(int x = 0; x < 10; x++)
{
// Linear search.
if(array[x].equals("Item I am looking for"))
{
//you've found the item. Let's stop.
item = array[x];
break;
}
}

在这个例子中什么更有意义。每次继续循环到10,即使在找到它之后,还是继续循环到10,直到找到该项并停止?或者用现实世界的术语来说,当你找到钥匙的时候,你还会继续寻找吗?

根据评论进行编辑

为什么不将 x设置为 11来中断循环?毫无意义。我们有 break!除非您的代码假设以后 x肯定比 10大(可能不应该这样) ,否则只要使用 break就可以了。

为了完整性而编辑

There are definitely other ways to simulate break. For example, adding extra logic to your termination condition in your loop. Saying that it is either loop pointlessly or use break isn't fair. As pointed out, a while loop can often achieve similar functionality. For example, following the above example..

while(x < 10 && item == null)
{
if(array[x].equals("Item I am looking for"))
{
item = array[x];
}


x++;
}

使用 break仅仅意味着您可以通过 for循环来实现这一功能。这还意味着,无论何时您希望循环的行为有所不同,都不必在终止逻辑中不断添加条件。比如说。

for(int x = 0; x < 10; x++)
{
if(array[x].equals("Something that will make me want to cancel"))
{
break;
}
else if(array[x].equals("Something else that will make me want to cancel"))
{
break;
}
else if(array[x].equals("This is what I want"))
{
item = array[x];
}
}

而不是一个终止条件如下的 while loop:

while(x < 10 && !array[x].equals("Something that will make me want to cancel") &&
!array[x].equals("Something else that will make me want to cancel"))

JLS 指定中断是循环的异常终止。然而,仅仅因为它被认为是不正常的,并不意味着它没有在许多不同的代码示例、项目、产品、航天飞机等中使用。JVM 规范没有说明是否存在性能损失,但是很明显,代码执行将在循环之后继续。

然而,代码的可读性可能会因为奇怪的中断而受到影响。如果你在一个复杂的 If 语句中插入了一个中断,这个复杂的 If 语句被一些副作用和奇怪的清理代码包围着,可能有一个带有标签的多级中断(或者更糟糕的是,一个接一个的奇怪的退出条件) ,那么对任何人来说都不容易阅读。

如果您希望通过强制迭代变量位于迭代范围之外,或者通过引入一种不必要的直接退出方式来中断循环,那么它的可读性比 break低。

然而,以空的方式循环额外的时间总是不好的做法,因为它需要额外的迭代,并且可能不清楚。

使用 break,就像实际上任何其他语言特性一样,在特定的上下文中,使用 可以是一种不好的做法,因为您显然在错误地使用它。但是,一些非常重要的习惯用法不能没有它来编码,或者至少会导致可读性差得多的代码。在这种情况下,break是可行的方法。

换句话说,不要听从任何关于 break或其他任何不合格的建议。我从来没有看到过仅仅为了实施一个“好的实践”而完全消瘦的代码。

关于您对性能开销的担忧,绝对没有。在字节码级别没有显式的循环结构: 所有的流控制都是按照条件跳转来实现的。

不,如果 达到一定的期望条件(比如找到匹配项)打破循环并不是一个坏习惯。很多时候,您可能想要停止迭代,因为您已经实现了您想要的,并且进一步迭代是没有意义的。但是,要小心,以确保您不是意外地错过了一些东西或突破时,不需要。

如果中断循环,这也可以是 提高绩效,而不是在循环的目的已经完成的情况下迭代成千上万条记录(也就是说,可能是为了匹配已经完成的所需记录)。

例如:

for (int j = 0; j < type.size(); j++) {
if (condition) {
// do stuff after which you want


break; // stop further iteration
}


}

使用 break in 循环可以是完全合法的,甚至可以是解决某些问题的唯一方法。

然而,它的坏名声来自于新程序员经常滥用它,导致混乱的代码,特别是使用 break 在本来可以在循环条件语句中编写的条件下停止循环。

虽然它不坏的做法,使用休息,并有许多优秀的用途,它不应该是所有你依赖。几乎任何中断的使用都可以写入循环条件中。当使用实际条件时,代码的可读性要高得多,但是在长时间运行或无限循环的情况下,中断非常有意义。如上所示,在搜索数据时它们也是有意义的。

如果事先知道循环必须在哪里停止,则可能会提高代码的可读性,以便在 forwhile`do-while循环中声明条件。

否则,这就是 break的确切用例。

这不是一个坏的实践,但是它可以使代码更不易读。解决这个问题的一个有用的重构方法是将循环移动到一个单独的方法,然后使用 return 语句而不是 break,例如:

String item;


for(int x = 0; x < 10; x++)
{
// Linear search.
if(array[x].equals("Item I am looking for"))
{
//you've found the item. Let's stop.
item = array[x];
break;
}
}

可以(使用 提取法)重构为:

public String searchForItem(String itemIamLookingFor)
{
for(int x = 0; x < 10; x++)
{
if(array[x].equals(itemIamLookingFor))
{
return array[x];
}
}
}

当从周围的代码调用它时,可以证明它更具可读性。

breakcontinue打破了读者的可读性,尽管它们通常很有用。 虽然不如“ goto”这个概念,但也差不多了。

此外,如果你使用一些像 Scala 这样的新语言(受 Java 和 Ocaml 这样的函数式编程语言的启发) ,你会注意到 breakcontinue就这样消失了。

特别是在函数式编程中,避免使用这种类型的代码:

为什么 scala 不支持 break 和 Continue?

To sum up: break and continueare widely used in Java for an imperative style, but for any coders that used to practice functional programming, it might be.. weird.

如果你开始做这样的事情,那么我会说它开始变得有点奇怪,你最好把它移动到一个独立的方法,returns在匹配的条件下得到一个结果。

boolean matched = false;
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++) {
if(matchedCondition) {
matched = true;
break;
}
}
if(matched) {
break;
}
}

要详细说明如何清理上面的代码,可以重构,将代码移动到 returns而不是使用 breaks的函数中。一般来说,这是更好地处理复杂/混乱的 breaks

public boolean  matches()
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++) {
if(matchedCondition) {
return true;
}
}
}
return false;
}

然而,对于一些简单的东西,如我下面的例子。使用 break的一切手段!

for(int i = 0; i < 10; i++) {
if(wereDoneHere()) { // we're done, break.
break;
}
}

在上面的例子中,改变条件 ij的值,只会使代码非常难以阅读。还有一种情况是上限(示例中为10)是变量,因此更难猜测为了退出循环应该将其设置为什么值。当然,您可以将 ij设置为 Integer.MAX _ VALUE,但是我认为您可以看到这很快就会变得混乱。:)

在我看来,For循环应该用于当一个固定数量的迭代将被做,他们不会被停止之前,每个迭代已经完成。在另一种情况下,如果你想早点退出,我更喜欢使用 While循环。即使你读了这两个小字,似乎更符合逻辑。一些例子:

for (int i=0;i<10;i++) {
System.out.println(i);
}

当我快速阅读这段代码时,我确信它会打印出10行,然后继续。

for (int i=0;i<10;i++) {
if (someCondition) break;
System.out.println(i);
}

这个我已经不太清楚了。为什么首先要进行10次迭代,然后在循环内部添加一些额外的条件来更快地停止?

我更喜欢以这种方式编写的前一个示例(即使它有点冗长,但只多了一行) :

int i=0;
while (i<10 && !someCondition) {
System.out.println(i);
i++;
}

每个读取这段代码的人都会立即看到,有一个额外的条件可能会提前终止循环。

当然,在非常小的循环中,您总是可以讨论每个程序员都会注意到 break 语句。但从我自己的经验来看,在更大的循环中,这些断裂是可以被监督的。(这就把我们带到了另一个话题,开始把代码分成更小的块)

在许多常见的情况下,break是表示算法的最自然的方式。它们被称为“ loop-and-a-half”结构; 范例如下

while (true) {
item = stream.next();
if (item == EOF)
break;
process(item);
}

如果你不能使用 break,你必须重复你自己:

item = stream.next();
while (item != EOF) {
process(item);
item = stream.next();
}

人们普遍认为,这种情况更糟。

类似地,对于 continue,有一个常见的模式如下:

for (item in list) {
if (ignore_p(item))
continue;
if (trivial_p(item)) {
process_trivial(item);
continue;
}
process_complicated(item);
}

这通常比链式 else if的替代方案更具可读性,特别是当 process_complicated不只是一个函数调用时。

返回文章页面循环退出和结构化编程: 重开辩论