Kotlin中Sequence重入状态恢复原理、与Iterator区分及自定义实现
咱们逐个来拆解你的问题,把这些点讲透:
1. Kotlin中Sequence在重入时如何恢复其状态?
其实准确来说,Sequence本身并不维护任何遍历状态——它本质是一个迭代器工厂。每次你开始遍历Sequence(比如用for循环、forEach,或者直接调用iterator()),它都会生成一个全新的Iterator实例,这个迭代器会独自维护遍历过程中的状态(比如当前遍历到哪一步、下一个元素怎么生成)。
举个直观的例子:
val numSeq = generateSequence(1) { it + 1 } // 第一次遍历,取前3个元素 numSeq.take(3).forEach { println(it) } // 输出1、2、3 // 第二次遍历,还是从1开始 numSeq.take(2).forEach { println(it) } // 输出1、2
两次遍历完全独立,因为每次都用了新的迭代器,不存在“恢复状态”的说法——而是每次都从零开始创建新的状态容器。
2. 为什么单个Sequence实例传入函数能生成所有字符组合,像是有状态副本?
这还是源于Sequence的迭代器工厂特性。你虽然只创建了一个Sequence实例,但每次在函数里触发遍历(比如for (c in seq)),都会调用Sequence的iterator()方法生成新的迭代器。每个迭代器都会独立执行Sequence的生成逻辑,相当于每次遍历都是一次“全新的计算”。
比如假设你的字符组合Sequence是这样的:
val charSeq = sequence { yield('a') yield('b') yield('c') } // 函数1遍历Sequence fun printChars(seq: Sequence<Char>) { seq.forEach { print("$it ") } } // 调用两次函数 printChars(charSeq) // 输出a b c printChars(charSeq) // 再次输出a b c
两次调用都能得到完整的字符序列,因为每次调用printChars时,都生成了新的迭代器,从头执行sequence块里的逻辑。这看起来像是Sequence有多个状态副本,但本质是每次遍历都新建了独立的状态载体(迭代器)。
3. Iterator执行相同操作无效?如何区分与自定义可恢复状态的类?
核心差异点
- Iterator:是单次遍历的状态机。它自身维护着唯一的遍历状态,一旦你遍历到末尾(
hasNext()返回false),就无法重置或重启遍历——再次调用next()会抛出NoSuchElementException,状态不会回到初始值。
比如:val iterator = listOf('a','b','c').iterator() iterator.forEach { print("$it ") } // 输出a b c iterator.forEach { print("$it ") } // 什么都不输出,因为迭代器已经耗尽 - Sequence:是迭代器的生产者。它不持有任何遍历状态,每次调用
iterator()都会返回一个全新的、初始状态的迭代器,因此每次遍历都是独立的,相当于“可恢复”的遍历能力。
如何自定义这类可恢复状态的类?
本质就是实现一个迭代器工厂,最常见的方式有两种:
方式1:自定义Sequence实现
直接继承Sequence<T>,重写iterator()方法,每次返回一个全新的自定义Iterator实例:
class FibonacciSequence : Sequence<Long> { override fun iterator(): Iterator<Long> { // 每次调用iterator()都返回新的迭代器,每个迭代器自己维护状态 return object : Iterator<Long> { private var prev = 0L private var current = 1L override fun hasNext(): Boolean = true // 无限序列 override fun next(): Long { val result = current current += prev prev = result return result } } } } // 使用示例 val fibSeq = FibonacciSequence() fibSeq.take(5).forEach { println(it) } // 输出1、1、2、3、5 fibSeq.take(3).forEach { println(it) } // 再次输出1、1、2
方式2:实现Iterable接口
因为Sequence本身也是Iterable<T>的子类型,你也可以直接实现Iterable<T>,效果类似:
class RepeatStringIterable(private val str: String, private val times: Int) : Iterable<String> { override fun iterator(): Iterator<String> { return object : Iterator<String> { private var count = 0 override fun hasNext(): Boolean = count < times override fun next(): String { count++ return str } } } } // 使用示例 val repeatIterable = RepeatStringIterable("hello", 3) repeatIterable.forEach { println(it) } // 输出hello、hello、hello repeatIterable.forEach { println(it) } // 再次输出hello、hello、hello
只要保证每次调用iterator()都返回新的、初始状态的迭代器,你的类就具备了“可恢复”的遍历能力。
内容的提问来源于stack exchange,提问作者Dims

