Java中避免使用continue和break的改写方法

今天在博客https://www.jianshu.com/p/7645a5ea7f46一文中看到博主对循环中continue和break的看法:尽量避免使用continue和break,觉得挺有意思,特记录下。


避免使用continue和break。循环语句(for,while)里面出现return是没问题的,然而如果你使用了continue或者break,就会让循环的逻辑和终止条件变得复杂,难以确保正确。

出现continue或者break的原因,往往是对循环的逻辑没有想清楚。如果你考虑周全了,应该是几乎不需要continue或者break的。如果你的循环里出现了continue或者break,你就应该考虑改写这个循环。改写循环的办法有多种:

  1. 如果出现了continue,你往往只需要把continue的条件反向,就可以消除continue。
  2. 如果出现了break,你往往可以把break的条件,合并到循环头部的终止条件里,从而去掉break。
  3. 有时候你可以把break替换成return,从而去掉break。
  4. 如果以上都失败了,你也许可以把循环里面复杂的部分提取出来,做成函数调用,之后continue或者break就可以去掉了。

下面我对这些情况举一些例子。

情况1:下面这段代码里面有一个continue:

List<String> goodNames = new ArrayList<>();
for (String name: names) {
  if (name.contains("bad")) {
    continue;
  }
  goodNames.add(name);
  ...
}  

它说:“如果name含有'bad'这个词,跳过后面的循环代码……” 注意,这是一种“负面”的描述,它不是在告诉你什么时候“做”一件事,而是在告诉你什么时候“不做”一件事。为了知道它到底在干什么,你必须搞清楚continue会导致哪些语句被跳过了,然后脑子里把逻辑反个向,你才能知道它到底想做什么。这就是为什么含有continue和break的循环不容易理解,它们依靠“控制流”来描述“不做什么”,“跳过什么”,结果到最后你也没搞清楚它到底“要做什么”。

其实,我们只需要把continue的条件反向,这段代码就可以很容易的被转换成等价的,不含continue的代码:

List<String> goodNames = new ArrayList<>();
for (String name: names) {
  if (!name.contains("bad")) {
    goodNames.add(name);
    ...
  }
}  

goodNames.add(name);和它之后的代码全部被放到了if里面,多了一层缩进,然而continue却没有了。你再读这段代码,就会发现更加清晰。因为它是一种更加“正面”地描述。它说:“在name不含有'bad'这个词的时候,把它加到goodNames的链表里面……”

情况2:for和while头部都有一个循环的“终止条件”,那本来应该是这个循环唯一的退出条件。如果你在循环中间有break,它其实给这个循环增加了一个退出条件。你往往只需要把这个条件合并到循环头部,就可以去掉break。

比如下面这段代码:

while (condition1) {
  ...
  if (condition2) {
    break;
  }
}

当condition成立的时候,break会退出循环。其实你只需要把condition2反转之后,放到while头部的终止条件,就可以去掉这种break语句。改写后的代码如下:

while (condition1 && !condition2) {
  ...
}

这种情况表面上貌似只适用于break出现在循环开头或者末尾的时候,然而其实大部分时候,break都可以通过某种方式,移动到循环的开头或者末尾。具体的例子我暂时没有,等出现的时候再加进来。

情况3:很多break退出循环之后,其实接下来就是一个return。这种break往往可以直接换成return。比如下面这个例子:

public boolean hasBadName(List<String> names) {
    boolean result = false;

    for (String name: names) {
        if (name.contains("bad")) {
            result = true;
            break;
        }
    }
    return result;
}

这个函数检查names链表里是否存在一个名字,包含“bad”这个词。它的循环里包含一个break语句。这个函数可以被改写成:

public boolean hasBadName(List<String> names) {
    for (String name: names) {
        if (name.contains("bad")) {
            return true;
        }
    }
    return false;
}

改进后的代码,在name里面含有“bad”的时候,直接用return true返回,而不是对result变量赋值,break出去,最后才返回。如果循环结束了还没有return,那就返回false,表示没有找到这样的名字。使用return来代替break,这样break语句和result这个变量,都一并被消除掉了。

我曾经见过很多其他使用continue和break的例子,几乎无一例外的可以被消除掉,变换后的代码变得清晰很多。我的经验是,99%的break和continue,都可以通过替换成return语句,或者翻转if条件的方式来消除掉。剩下的1%含有复杂的逻辑,但也可以通过提取一个帮助函数来消除掉。修改之后的代码变得容易理解,容易确保正确。



作者:正义的花生
链接:https://www.jianshu.com/p/7645a5ea7f46
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值