5. 循环控制:for循环、while循环、do-while循环、break与continue、标签与返回
循环,说白了就是让代码反复干同一件事。你想想看,如果让你打印1到100,你总不会写100行println吧?循环就是干这个的。
Kotlin的循环设计得挺讲究,既有传统的while,也有更现代的for-in。我个人习惯是:能用for的地方绝不用while,因为for更安全、更不容易写出死循环。
5.1 for循环:遍历的利器
Kotlin的for循环和Java差别很大。它没有传统的for (int i = 0; i < 10; i++)这种写法,而是用for (item in collection)的形式。我第一次从Java转过来时还有点不习惯,但用了一周就真香了。
// 遍历区间
for (i in 1..5) {
println(i) // 输出 1 2 3 4 5
}
// 遍历数组
val arr = intArrayOf(10, 20, 30)
for (item in arr) {
println(item)
}
// 带索引遍历
for ((index, value) in arr.withIndex()) {
println("arr[$index] = $value")
}
这里有个细节:1..5是闭区间,包含5。如果你不想包含结尾,用1 until 5,那就是1到4。
5.2 while循环:条件驱动
while循环和Java完全一样。当条件为true时,反复执行代码块。什么时候用while?当你不知道要循环多少次的时候。比如读取文件直到末尾,或者等待某个状态变化。
var x = 10
while (x > 0) {
println(x)
x--
}
// 读取输入直到输入"exit"
var input: String?
while (readLine().also { input = it } != "exit") {
println("你输入了: $input")
}
5.3 do-while循环:至少执行一次
do-while和while的区别就一句话:do-while至少执行一次,然后再判断条件。适合那些「不管怎样先干一次再说」的场景。
var count = 5
do {
println("还剩 $count 次")
count--
} while (count > 0)
// 一个实际场景:先发送请求,如果失败则重试
var retry = 3
do {
println("尝试发送请求...")
// 模拟请求
retry--
} while (retry > 0)
嗯,这里要注意:do-while用得不多,但用对地方很顺手。比如用户输入验证,先让用户输入一次,不对再提示重新输入。
5.4 break与continue:控制循环节奏
break是「到此为止,全退出」,continue是「这次算了,继续下一次」。这两个关键字在任何语言里都差不多,但Kotlin里有个小增强——break和continue可以配合标签使用。
// break 示例:找到第一个偶数就停
for (i in 1..10) {
if (i % 2 == 0) {
println("找到偶数: $i")
break
}
}
// continue 示例:跳过奇数
for (i in 1..10) {
if (i % 2 != 0) continue
println(i) // 只输出偶数
}
5.5 标签与返回:跳出多层循环
这是Kotlin比较有特色的地方。当你嵌套循环时,break和continue默认只作用于最内层循环。如果你想跳出外层循环,就需要标签。
outer@ for (i in 1..3) {
for (j in 1..3) {
if (i == 2 && j == 2) {
println("跳出外层循环")
break@outer
}
println("i=$i, j=$j")
}
}
// 输出:
// i=1, j=1
// i=1, j=2
// i=1, j=3
// i=2, j=1
// 跳出外层循环
标签的语法很简单:在循环前面写标签名@,然后在break或continue后面加上@标签名。我个人觉得这个设计比Java的「标记块+break」要直观得多。
5.6 循环中的return:不只是跳出循环
在Kotlin中,return在普通循环里会直接退出整个函数。这和Java一样。但如果你在lambda表达式中使用return,情况就不同了——它默认是「非局部返回」,也就是退出外层函数。
fun findFirstEven(numbers: List<Int>): Int? {
for (n in numbers) {
if (n % 2 == 0) return n // 直接退出函数
}
return null
}
// lambda中的return
fun processList(list: List<Int>) {
list.forEach {
if (it == 0) return // 这会退出processList函数,不是只退出forEach
println(it)
}
println("这行可能不会执行")
}
如果你只想退出lambda而不是外层函数,用return@forEach,这其实就是标签的另一种形式。
5.7 知识体系总览
下面这张图把Kotlin循环控制的核心知识点串起来了。我建议你把它当作复习地图,每次写循环前扫一眼,能帮你选对工具。
5.8 实战对比:什么时候用哪个?
| 场景 | 推荐循环 | 原因 |
|---|---|---|
| 遍历数组/列表 | for-in | 最简洁,不会越界 |
| 遍历区间数字 | for-in + 区间 | 语法直观,支持步长 |
| 条件未知的重复 | while | 灵活,条件可动态变化 |
| 至少执行一次 | do-while | 保证首次执行 |
| 嵌套循环提前退出 | for + 标签 | 精准控制跳出层级 |
| 集合操作(过滤/映射) | forEach + lambda | 函数式风格,更安全 |
return@forEach才是正确的做法。
好了,循环控制这块就这些。说白了就是三件事:选对循环类型、用好控制语句、记住标签的威力。写代码时多想想「这个场景用哪个最合适」,慢慢就会形成肌肉记忆。
公众号:蓝海资料掘金营,微信deep3321