关于 kotlin 的 forEach 如何实现 break/continue 的思考

在 kotlin 的 forEach 如何实现 break 的效果?
官网文档的给出的代码是这样的

fun foo() {
    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")
}
------------输出---------------
循环中……
1,
循环中……
------------------------------ 

⚠️ 看效果好像和 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")
}
------------输出---------------
循环中……
1,
循环中……
循环中……
3,
循环中……
4,
循环中……
5,
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")
}
------------输出---------------
循环中……
1,
循环中……
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)
        }
    }
    ……
}
------------输出---------------
0
1
------------------------------ 

我们用着 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) }
    ……
}
------------输出---------------
0
1
------------------------------ 

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

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

参考文档

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

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

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值