Kotlin序列如何实现包含匹配元素的takeWhileInclusive?
我太懂这种差一步的憋屈了!takeWhile明明几乎完美,偏偏就把我们要留的那个关键元素给漏掉。针对你的需求,给你几个符合要求的方案——都是函数式风格,不用可变变量,最多几步链式调用就能搞定:
方案1:用Sequence构建器(一次遍历,简洁高效)
这个方法直接用Kotlin的sequence构建器实现“取到匹配元素就停止,且保留它”的逻辑,全程函数式,代码超简洁还不会重复遍历序列:
sequence { generateSequence(obj::class.java, Class<*>::getSuperclass).forEach { clazz -> yield(clazz) if (interestedType in clazz.interfaces) return@sequence } }.joinToString(" > ").run(::println)
逻辑非常直观:遍历原序列的每个元素,先把它加入结果序列(yield),如果当前元素匹配我们的条件,直接终止遍历——完美保留了第一个匹配元素,还只走一遍序列。
方案2:takeWhile + firstOrNull(链式调用,无需自定义扩展)
如果你不想用构建器,也可以通过两次懒加载的序列操作组合实现,短序列场景下性能完全没问题:
generateSequence(obj::class.java, Class<*>::getSuperclass) .let { seq -> seq.takeWhile { interestedType !in it.interfaces } + seq.firstOrNull { interestedType in it.interfaces } } .filterNotNull() .joinToString(" > ") .run(::println)
这里用let把原序列暂存,先取所有不匹配的元素,再追加第一个匹配的元素,最后过滤掉可能的null(防止序列到头都没找到匹配元素的情况)。因为序列是懒加载的,实际只会遍历到第一个匹配元素的位置,不会做多余操作。
方案3:自定义扩展函数(复用性强,代码更优雅)
如果这个需求你经常遇到,不如自己实现一个安全的takeUntilInclusive扩展函数——比你之前看到的var版本更函数式,也完全不存在并发风险:
fun <T> Sequence<T>.takeUntilInclusive(predicate: (T) -> Boolean): Sequence<T> { return sequence { for (element in this@takeUntilInclusive) { yield(element) if (predicate(element)) break } } }
调用起来就像标准库函数一样丝滑:
generateSequence(obj::class.java, Class<*>::getSuperclass) .takeUntilInclusive { interestedType in it.interfaces } .joinToString(" > ") .run(::println)
这个扩展完全依赖Kotlin序列的单线程顺序迭代特性,所谓“假设顺序执行”的注释其实是多余的——序列的迭代本身就是顺序执行的,所以这个实现安全可靠。
内容的提问来源于stack exchange,提问作者Roland
相关产品推荐
相关产品推荐

