• Kotlin | 在for、forEach循环中正确的使用break、continue


    Kotlin 有三种结构化跳转表达式:

    • return:默认从最直接包围它的函数或者匿名函数返回。
    • break:终止最直接包围它的循环。
    • continue:继续下一次最直接包围它的循环。

    for循环中使用break、continue

     for (i in 1..5) {
        if (i == 3) break //1 这里分别使用break continue return
        println("i: $i")
     }
     println("循环外继续执行")
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1处分别使用break、continue、return 替换,执行结果如下:

    //break 
    i: 1
    i: 2
    循环外继续执行
    
    //continue
    i: 1
    i: 2
    i: 4
    i: 5
    循环外继续执行
    
    //return
    i: 1
    i: 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    嗯,跟Java中的使用姿势是一样的,继续往下看。

    Label标签

    在 Kotlin 中任何表达式都可以用标签(label)来标记。 标签的格式为标识符后跟 @ 符号,例如:abc@、loop@都是有效的标签。 要为一个表达式加标签,我们只要在其前加标签即可。示例:

    loop@ for (i in 1..5){
     //...
    }
    
    • 1
    • 2
    • 3

    这里在嵌套for循环中使用Label,可以控制break及continue的范围:

    loop@ for (i in 1..2) {
        println("i: $i")
        for (j in 1..5) {
            if (j == 3) break@loop //break continue
            println("j: $j")
        }
    }
    println("循环外继续执行")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    执行结果:

    //break
    i: 1
    j: 1
    j: 2
    循环外继续执行
    
    //continue
    i: 1
    j: 1
    j: 2
    
    i: 2
    j: 1
    j: 2
    循环外继续执行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    结论:Label标签限制的 break 跳转到刚好位于该标签指定的循环后面的执行点。 continue 继续标签指定的循环的下一次迭代

    注意不能在上述代码中使用return@loop,因为目标标签表示的不是函数,错误信息如下:

    Target label does not denote a function

    forEach中模拟break、continue

    在forEach中并不能直接使用break、continue:

    break/continue错误使用
    可以看到直接报错了,错误信息也很明确:break 和 continue 只允许在循环中使用,而这里是forEach的闭包,所以并不能直接使用break 和 continue。那么如何在forEach中分别模拟出break、continue的效果呢?通过Label即可实现,如:

    fun forEachControl() {
        listOf(1, 2, 3, 4, 5).forEach forEach@{
            if (it == 3) return@forEach
            println("it:$it")
        }
        println("循环外继续执行")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    return 只会从 lambda 表达式中返回。通常情况下使用隐式标签更方便(Label 标签与接受该 lambda 的函数同名即可使用隐式标签),简化之后:

    fun forEachControl() {
        listOf(1, 2, 3, 4, 5).forEach{
            if (it == 3) return@forEach
            println("it:$it")
        }
        println("循环外继续执行")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    代码执行结果:

    it:1
    it:2
    it:4
    it:5
    循环外继续执行
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看到return@forEach相当于表达式里面的continue了。嗯哼?为什么不是break的效果呢?明明已经return@forEach了呀,其实这是Kotlin闭包带来的副作用,看下forEach的源码:

    public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
        //注意看下面这行代码的逻辑
        for (element in this) action(element)
    }
    
    • 1
    • 2
    • 3
    • 4

    action函数作为参数传入的,所以在forEachreturn@forEach 只能停止当前闭包的逻辑,后面的循环并不会受影响,会继续后面的循环。那么如何在forEach中模拟break的效果呢?只要把声明Label放到forEach外面即可以了

    fun forEachControl() {
        run loop@{//1
           listOf(1, 2, 3, 4, 5).forEach {
              if (it == 3) return@loop//2
              println("it:$it")
            }
              println("循环外继续执行")
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    执行结果:

    it:1
    it:2
    
    • 1
    • 2

    可以看到1处的Label标签放到了forEach的外层了,那么当执行2处的return@loop时会直接跳出forEach循环,进而实现了break功能。

    资料

    【1】Kotlin返回和跳转:https://www.kotlincn.net/docs/reference/returns.html

  • 相关阅读:
    【docker/K8S】docker/K8S安装mysql的坑-20220815
    【DevOps】Git 图文详解(四):Git 使用入门
    15:00面试,15:08就出来了,问的问题有点变态。。。
    JAVA_多线程的实现方式
    HDFS的垃圾回收机制
    MySQ 学习笔记
    基于Debian搭建Hyperledger Fabric 2.4开发环境及运行简单案例
    SpringMVC_拦截器
    解决Mapper接口错误: 使用MyBatis Plus时未正确继承BaseMapper接口或添加@Mapper注解导致无法使用相关方法的探索与编程实践
    2023年天津农学院专升本专业课参考教材
  • 原文地址:https://blog.csdn.net/u013700502/article/details/132978143