run loop@ { listOf ( 1 , 2 , 3 , 4 , 5 ) . forEach { if ( it == 3 ) return @loop // 从传入 run 的 lambda 表达式非局部返回 print ( it ) print ( " done with nested loop" )

😲 这是什么鬼代码

先回顾一下break/continue

kotlin 的 break/continue 和 java 的作用是一样的

fun testBreak() {
    for (i in 1..5) {
        if (i > 3) break
        println(i)
    println("end")
------------输出---------------
1,2,3,end
------------------------------    
fun testContinue() {
    for (i in 1..5) {
        if (i == 3) continue
        println(i)
    println("end")
------------输出---------------
1,2,4,5,end
------------------------------    

在 kotlin 中可以对表达式设置标签 以标签名加 @ 的形式,例如

loop@ for(i in 1..10){……}
 

loop 就是我们指定的标签名,后面紧跟一个 @ 符号

标签的作用,可以让我们返回或跳转到对应标签的位置,例如两层循环

fun testLabel() {
  // ⬇️  最外层循环设置一个loop1标签
   loop1@ for (i in 1..5) {
        for (j in 1..3) {
            if (j>1)  break@loop1 // 0️⃣
            print("($i,$j),")
        println()
        print("i=$i,")
    print("end")
------------输出---------------
(1,1),end
------------------------------    
 

在代码 0️⃣ 处 break@loop1 那么就是终止最外层循环,如果 0️⃣ 处写的是 break 那么只会终止内部的循环,外部循环还会继续的。

forEach如何使用break/continue

在普通的 for 循环中利用 break/continue 可以很好的控制,那 forEach 该如何使用
一般使用 forEach 如下所示

fun testForEach(){
    listOf(1,2,3,4,5).forEach {
        print("$it,")
    print("end")
------------输出---------------
1,2,3,4,5,end
------------------------------ 
 

你会发现在 forEach 里面 break/continue 都不能用,那怎么终止循环或跳过这次循环呢 ?

在 forEach 可以使用 return ,其效果如下

fun testForEach(){
    listOf(1,2,3,4,5).forEach {
        println("循环中……")
        if (it==2) return
        println("$it,")
    print("end")
------------输出---------------
循环中……
循环中……
------------------------------ 
 

⚠️ 看效果好像和 break 一样,循环终止了没有执行, 但仔细看会发现 ”end“ 没有输出,这是怎么回事?这里的 return 其实是函数的 return 函数如果 return 了,那么 return 下面的都不会执行,所以 ”end“ 没有打印。

我们修改一下代码再看一下

fun testForEach(){
    listOf(1,2,3,4,5).forEach {
        println("循环中……")
        if (it==2) return@forEach
        println("$it,")
    print("end")
------------输出---------------
循环中……
循环中……
循环中……
循环中……
循环中……
------------------------------ 
 

这次写的是 return@forEach 表示是是否终止这次 lambda 的进行执行,for 循环还会继续,这种写法和 continue 的效果是一致的

👇 这是kotlin中的forEach源码

public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
 

forEach 源码很简单,就是循环执行 action 这个函数,这个 action 就是我们传入的 lambda,所有我们 return@forEach 只会影响一次,整体的 for 循环不会被终止的。

return@forEach 和 continue 效果一致,而 return 会让整个函数终止,那么要实现 break 该怎么办,官网文档说可以增加另一层嵌套 lambda 表达式并从其中非局部返回来模拟

fun testForEach(){
   run {
       listOf(1,2,3,4,5).forEach{
           println("循环中……")
           if (it==2) return@run
           println("$it,")
    print("end")
------------输出---------------
循环中……
循环中……
------------------------------ 
 

这样看来和 break 效果一致,但感觉不够优雅呀。

思考 🤔

为何在 forEach 想使用 break/continue 就那么麻烦 ?
为何 kotlin 不在 forEach 里面支持 break/continue ?
我们对集合进行遍历,配合 break/continue 然后写一些逻辑 , 其目的一般都是操作集合而写的逻辑。

假设有这么一个问题:
给定一个集合如 [0,1,2,3,4,5] (集合中一定会有2这个元素)把元素为 2 之前的元素遍历出来
按照上面我们说的方式使用forEach实现如下

fun testForEach() {
    run {
        listOf(0,1, 2, 3, 4, 5).forEach{
            if (it == 2) return@run
            println(it)
------------输出---------------
------------------------------ 

我们用着 forEach 这种高阶函数,却按照以前的思考的方式去写代码,显然代码不够优雅。条条大路通罗马,既然 forEach 对 break/continue 那么不友好,我们能不能换种思路去看一下问题呢。问题给定一个集合输出集合中的一部分。这一输入输出的不经让我想起了函数式编程,毕竟 kotlin 也是支持函数式编程的语言,而且在集合框架中提供了需要操作的函数。

![image.png](https://img-blog.csdnimg.cn/img_convert/cfc9cf7d220bb42978df0de57b0c8936.png#align=left&display=inline&height=761&margin=[object Object]&name=image.png&originHeight=761&originWidth=346&size=48846&status=done&style=none&width=346)

上图内容是 kotlin 中文网文档集合部分的目录,从目录可以看出,提供的函数式相当丰富的。

如此多的函数,难道没有合适的吗?当然有 。下面使用takeWhile 这个函数来实现上述的问题:

fun testForEach() {
    listOf(0, 1, 2, 3, 4, 5).takeWhile { it != 2 }.forEach { println(it) }
------------输出---------------
------------------------------ 
 

takeWhile 表示从头开始取,一直取到不满足条件的,所以从头开始2之前的元素都被取出来了。使用集合框架api的就可以很轻松的搞定。

上面我说的这个例子可能有些牵强,但我觉得这个值得我们去思考,当我们去处理一个集合的时候,可以先想想集合框架提供的函数是否可以解决,以函数式编程的方式,而不是在 forEach 中找 break/continue 的替代方案,毕竟 kotlin 也是支持函数式编程的语言,我们可以用函数式编程风格操作集合代替以前的方案。

返回与跳转:break 与 continue - Kotlin 语言中文站

取集合的一部分 - Kotlin 语言中文站

forEachcontinuebreak 如何写 listOf(1, 2, 3, 4, 5).forEach { if (it == 3) return@forEach print("$it ") println("\n") run breaking@{ listOf(1, 2, 3, 4, 5).forEach { if (it == 3) return@breaking