1. 简介

在 Kotlin 中,不能像传统循环那样直接使用 breakcontinue 来跳出或跳过函数式循环(如 forEach)。

例如,我们不能在 forEach 中直接使用 break 来终止循环。

但幸运的是,Kotlin 提供了一些技巧来模拟这种行为。下面我们来详细看看这些方法。

2. 使用标签(Labels)

我们知道,Kotlin 支持通过标签(label)来控制 return 的作用范围。我们可以利用这个特性来模拟 continuebreak 的行为。

2.1. 模拟 continue

示例代码如下:

val list = listOf(3, 4, 3, 4, 3)
var sum = 0
list.forEach loop@{ number ->
    if (number % 2 == 0) { // skip all even numbers
        return@loop
    }
    sum += number
}
assertTrue { sum == 9 }

说明:

  • 我们给 forEach 加了一个标签 loop@
  • 当满足条件时,使用 return@loop 跳出当前迭代,模拟 continue
  • 这样就跳过了偶数的处理。

⚠️ 注意: 这种方式只能跳过当前迭代,不能提前终止整个循环。

2.2. 模拟 break

示例代码如下:

run outer@{
    list.forEach inner@{ number ->
        if (number % 2 == 0) { // 'break' at the first even number
            return@outer
        }
        sum += number
    }
}
assertTrue { sum == 3 }

说明:

  • 我们用 run 创建了一个外层作用域,并打上标签 outer@
  • forEach 内部判断条件后,使用 return@outer 跳出整个外层作用域,从而模拟 break
  • 这样就能提前终止整个循环。

⚠️ 注意: 如果直接 return 不加标签,会直接跳出当前函数,可能导致后续代码不执行。所以这里加标签是为了更精确地控制跳转目标。

3. 使用 filter 和 takeWhile

另一种方式是使用函数式操作链,比如 filtertakeWhile,间接实现类似 continuebreak 的效果。

3.1. 用 filter 实现 continue

val list = listOf(3, 4, 3, 4, 3)
var sum = 0
list.filter { it % 2 != 0 } // skip all even numbers
    .forEach { number ->
        sum += number
    }
assertTrue { sum == 9 }

说明:

  • 先通过 filter 过滤掉不需要处理的元素(如偶数)。
  • 再对剩下的元素执行 forEach
  • 效果上等同于在传统循环中使用 continue

3.2. 用 takeWhile 实现 break

list.takeWhile { it % 2 != 0 } // 'break' at the first even number
    .forEach { number ->
        sum += number
    }
assertTrue { sum == 3 }

说明:

  • takeWhile 会保留满足条件的元素,一旦遇到不满足条件的元素,后面的全部跳过。
  • 这样就模拟了在遇到某个元素时提前结束循环的效果。

⚠️ 注意: takeWhile 是一次性收集所有满足条件的元素,不能像 break 那样逐个判断。如果逻辑依赖逐个判断的顺序,要特别注意。

4. 总结

方法 优点 缺点
标签 + return 性能高,无需创建中间集合 语法略复杂
filter + takeWhile 代码简洁,易读 会创建中间集合,可能有性能开销

建议:

  • 如果对性能敏感(如处理大数据集),推荐使用 标签 + return
  • 如果代码逻辑简单,追求可读性,可以用 filter + takeWhile

最后,完整的示例代码可以在这里查看:GitHub 仓库地址


原始标题:Break or Continue a Functional Loop in Kotlin