在Java中使用break来退出循环是不好的做法吗?
我想知道是否使用break
语句退出循环而不是执行循环条件是一个“不好的做法”?
我没有足够的了解Java和JVM知道如何处理循环,所以我想知道如果我这样做是忽略了一些重要的东西。
这个问题的重点是:是否有一个特定的性能开销?
好主不。 有时在满足总体要求的循环中可能会出现某些情况,而不满足逻辑循环条件。 在这种情况下,使用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次,即使find了它,或者循环直到find该项目并停止? 或者把它变成现实世界的条件; 当你find你的钥匙,你继续看?
编辑回应评论
为什么不把x
设置为11
来打破循环? 没有用。 我们break
! 除非你的代码是假设x
肯定大于10
以后(而且可能不应该是这样),那么你就可以使用break
。
为了完整性而编辑
绝对有其他方法来模拟break
。 例如,在循环中添加额外的逻辑到您的终止条件。 说无论是循环还是使用break
都是不公平的。 正如所指出的,while循环通常可以实现类似的function。 例如,按照上面的例子..
while(x < 10 && item == null) { if(array[x].equals("Item I am looking for")) { item = array[x]; } x++; }
简单地使用break
意味着你可以用for
循环来实现这个function。 这也意味着,只要您希望循环的行为不同,就不必在条件中添加终止逻辑。 例如。
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"))
使用break
,就像其他任何语言function一样,在特定的情况下,如果明显滥用, 可能是不好的做法。 但是一些非常重要的成语不能没有它,或至less会导致更less的可读代码。 在这些情况下, break
是要走的路。
换句话说,不要听任何一揽子,不合格的build议 – 关于break
或其他事情。 我从来没有见过这样的代码完全憔悴,只是为了强制实施一个“良好的做法”。
关于你对性能开销的关注,绝对没有。 在字节码级别,无论如何都没有显式的循环结构:所有的stream程控制都是通过条件跳转来实现的。
JLS指定一个中断是一个循环的exception终止。 但是,仅仅因为它被认为是exception的,并不意味着它不被用于许多不同的代码示例,项目,产品,太空穿梭机等等.JVM规范并没有说明是否存在性能损失,虽然它是在循环之后清除代码执行将继续。
但是,代码可读性可能会遭遇奇怪的中断。 如果你坚持在一个复杂的if语句中包含副作用和奇怪的清理代码,可能是带有标签的多层次中断(或者更糟糕的是,具有一组奇怪的退出条件),那么它不会任何人都可以轻松阅读。
如果你想通过强制迭代variables超出迭代范围来打破循环,或者通过其他方式引入不必要的直接退出方式,那么它的可读性就不如break
。
然而,以空方式循环多余的时间几乎总是不好的做法,因为它需要额外的迭代并且可能不清楚。
使用break in循环可以是完全合法的,甚至可以是解决某些问题的唯一方法。
然而,新的程序员通常会滥用它,导致代码混淆,特别是通过使用break来停止循环条件语句中可能已经写入循环条件语句的循环,这是不好的声誉。
在我看来,当一个固定的迭代次数完成时,应该使用一个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次迭代,但是在循环中join一些额外的条件以便尽早停止?
我更喜欢前面用这种方式编写的例子(即使它稍微冗长一点,但只有1行更多):
int i=0; while (i<10 && !someCondition) { System.out.println(i); i++; }
每个阅读这段代码的人都会立即看到有一个额外的条件可能会提前终止循环。
当然,在非常小的循环中,你总是可以讨论每个程序员都会注意到break语句。 但是我可以从我自己的经验中得知,在更大的循环中,这些rest可以被监督。 (而这又把我们带到了另一个话题上,开始把代码分成小块)
不,如果达到了某些所需的条件 (如发现匹配),跳出循环并不是一个错误的做法。 很多时候,你可能想停止迭代,因为你已经达到了你想要的,没有任何重复的地步。 但是,要小心,以确保你不会意外地丢失东西或不需要的时候爆发。
如果你打破了循环,而不是遍历数千条logging,即使循环的目的是完整的(即可能匹配所需的logging已经完成),也可以增加性能改进 。
例如:
for (int j = 0; j < type.size(); j++) { if (condition) { // do stuff after which you want break; // stop further iteration } }
这不是不好的做法,但会使代码更不可读。 一个有用的重构来解决这个问题是将循环移动到一个单独的方法,然后使用返回语句,而不是一个rest,例如(例如从@克里斯的答案举起):
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]; } } }
当从周围的代码调用时,可以certificate更具可读性。
如果你开始做这样的事情,那么我会说它开始变得有点奇怪,你最好把它移动到一个单独的方法,在matchedCondition 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; } }
改变条件,在上述情况下, i
和j
的价值,你只是使代码真的难以阅读。 也有可能出现这样的情况,即上限(例子中的10)是variables,所以更难猜测为了退出循环而将其设置为什么值。 你当然可以设置i
和j
Integer.MAX_VALUE,但我认为你可以看到这开始变得凌乱得很快。 🙂
有许多常见的情况, break
是expressionalgorithm的最自然的方式。 他们被称为“循环半”的结构; 范例是
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
不仅仅是一个函数调用的时候,这通常比带有链接的选项更具可读性。
进一步阅读: 循环退出和结构化编程:重新开放辩论
不,这不是一个坏习惯。 这是最简单和有效的方法。
虽然它不错的做法是使用中断,并有很多优秀的用途,它不应该只能依靠。 几乎任何中断的使用都可以写入循环条件。 在使用实际条件时,代码更具可读性,但在长时间运行或无限循环的情况下,rest是非常有意义的。 如上所示,search数据时也是有意义的。
如果事先知道循环将不得不停止的地方,那么可能会提高代码的可读性,以便在for
, while
或`do-while
循环中声明条件。
否则,这是break
的确切用例。
break
并continue
打破读者的可读性,虽然它通常是有用的。 没有“goto”的概念,但几乎。
另外,如果你采用Scala这样的新语言(受到Java和Ocaml等函数式编程语言的启发),你会注意到break
和continue
简单的消失了。
特别是在函数式编程中,避免了这种代码风格:
为什么scala不支持break和continue?
总结一下: break
和continue
在Java中被广泛的使用,但是对于那些用来实现函数式编程的编程人员来说,这可能是奇怪的。