Kotlin 序列(Sequence)深入学习
什么是序列?
序列(Sequence)是 Kotlin 中一种惰性求值的集合,类似于 Java 8 的 Stream。它按需计算元素,可以处理无限数据流,并且避免了中间集合的创建。
创建序列
1. sequenceOf() - 创建固定序列
val seq1 = sequenceOf(1, 2, 3, 4, 5)
2. asSequence() - 集合转序列
val list = listOf(1, 2, 3, 4, 5)
val seq2 = list.asSequence()
3. generateSequence() - 生成无限/有限序列
// 无限序列(从1开始,每次+1)
val infiniteSeq = generateSequence(1) { it + 1 }
// 有限序列(指定种子和终止条件)
val finiteSeq = generateSequence(1) { if (it < 10) it + 1 else null }
// 生成斐波那契数列
val fibonacci = generateSequence(Pair(0, 1)) {
Pair(it.second, it.first + it.second)
}.map { it.first }
4. sequence{} - 使用构建器
val seq = sequence {
yield(1) // 产生单个值
yieldAll(listOf(2, 3, 4)) // 产生集合
yieldAll(generateSequence(5) { it + 1 }.take(2)) // 产生其他序列
}
// 结果:1, 2, 3, 4, 5, 6
序列 vs 集合:关键区别
// 集合操作(急切求值)
val listResult = listOf(1, 2, 3, 4, 5)
.map {
println("map: $it")
it * it
}
.filter {
println("filter: $it")
it > 10
}
// 输出:所有元素先经过map,再经过filter
// 序列操作(惰性求值)
val seqResult = listOf(1, 2, 3, 4, 5)
.asSequence()
.map {
println("map: $it")
it * it
}
.filter {
println("filter: $it")
it > 10
}
.toList() // 末端操作触发计算
// 输出:每个元素依次经过map和filter
中间操作(Intermediate Operations)
常用中间操作:
val seq = (1..10).asSequence()
// 1. map - 转换
val doubled = seq.map { it * 2 }
// 2. filter - 过滤
val even = seq.filter { it % 2 == 0 }
// 3. take / drop - 取/丢弃前n个
val first3 = seq.take(3)
val after3 = seq.drop(3)
// 4. takeWhile / dropWhile - 条件取/丢弃
val whileLessThan5 = seq.takeWhile { it < 5 }
val dropWhileLessThan5 = seq.dropWhile { it < 5 }
// 5. distinct - 去重
val distinctSeq = sequenceOf(1, 2, 2, 3, 3, 3).distinct()
// 6. sorted / sortedBy - 排序
val sorted = sequenceOf(3, 1, 4, 1, 5).sorted()
val sortedByLength = sequenceOf("apple", "banana", "kiwi")
.sortedBy { it.length }
// 7. flatMap - 扁平化
val flatMapped = seq.flatMap {
sequenceOf(it, it * 10)
}
操作顺序的重要性:
// 顺序影响性能
val numbers = (1..1_000_000).asSequence()
// 好的顺序:先过滤,再转换
val good = numbers
.filter { it % 2 == 0 } // 减少元素数量
.map { it * it } // 只对剩余元素计算
.take(10)
// 差的顺序:先转换,再过滤
val bad = numbers
.map { it * it } // 对100万个元素计算
.filter { it % 2 == 0 } // 再过滤
.take(10)
末端操作(Terminal Operations)
常用末端操作:
val seq = (1..10).asSequence()
// 1. 转换为集合
val list = seq.toList()
val set = seq.toSet()
val map = seq.associateWith { it * 2 }
// 2. 聚合操作
val sum = seq.sum()
val avg = seq.average()
val min = seq.minOrNull()
val max = seq.maxOrNull()
val count = seq.count()
// 3. 查找元素
val first = seq.first()
val firstEven = seq.first { it % 2 == 0 }
val last = seq.last()
val element = seq.elementAt(3)
val find = seq.find { it > 5 }
// 4. 判断
val any = seq.any { it > 5 } // 是否有元素满足条件
val all = seq.all { it > 0 } // 是否所有元素满足条件
val none = seq.none { it < 0 } // 是否没有元素满足条件
// 5. 遍历
seq.forEach { println(it) }
seq.forEachIndexed { index, value ->
println("$index: $value")
}
// 6. 规约
val sumReduce = seq.reduce { acc, value -> acc + value }
val sumFold = seq.fold(0) { acc, value -> acc + value }
高级序列操作
1. windowed - 滑动窗口
val seq = (1..5).asSequence()
val windows = seq.windowed(3)
// [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
val windowsPartial = seq.windowed(3, partialWindows = true)
// [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5], [5]]
2. zipWithNext - 相邻元素配对
val seq = (1..5).asSequence()
val pairs = seq.zipWithNext()
// [(1, 2), (2, 3), (3, 4), (4, 5)]
val transformed = seq.zipWithNext { a, b -> a + b }
// [3, 5, 7, 9]
3. chunked - 分块
val seq = (1..10).asSequence()
val chunks = seq.chunked(3)
// [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
val chunksTransformed = seq.chunked(3) { it.sum() }
// [6, 15, 24, 10]
4. plus / minus - 序列合并
val seq1 = sequenceOf(1, 2, 3)
val seq2 = sequenceOf(4, 5, 6)
val combined = seq1 + seq2 // 1, 2, 3, 4, 5, 6
val minus = seq1 - sequenceOf(2) // 1, 3
无限序列处理
// 1. 无限序列的创建
val naturalNumbers = generateSequence(0) { it + 1 }
// 2. 限制无限序列
val first10 = naturalNumbers.take(10).toList()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// 3. 条件终止
val until100 = generateSequence(0) {
if (it < 100) it + 1 else null
}
// 4. 生成质数序列
fun primes() = generateSequence(2) { current ->
generateSequence(current + 1) { it + 1 }
.first { candidate ->
(2..Math.sqrt(candidate.toDouble()).toInt())
.none { candidate % it == 0 }
}
}
性能优化示例
示例1:处理大文件
fun processLargeFile(file: File) {
val linesSequence = file.useLines { it.asSequence() }
val result = linesSequence
.filter { it.isNotBlank() }
.map { it.trim() }
.filter { it.startsWith("ERROR") }
.take(100) // 只取前100个错误
.toList() // 触发计算
}
示例2:分页查询模拟
fun pagedSequence(pageSize: Int = 10): Sequence<String> = sequence {
var page = 0
while (true) {
val items = fetchPage(page, pageSize) // 模拟数据库查询
if (items.isEmpty()) break
yieldAll(items)
page++
}
}
fun fetchPage(page: Int, size: Int): List<String> {
// 模拟数据库分页查询
val start = page * size
val end = start + size
return (start until end)
.map { "Item $it" }
.takeIf { it.isNotEmpty() } ?: emptyList()
}
自定义序列
// 自定义序列实现
class RangeSequence(
private val start: Int,
private val end: Int
) : Sequence<Int> {
override fun iterator(): Iterator<Int> = object : Iterator<Int> {
private var current = start
override fun hasNext(): Boolean = current <= end
override fun next(): Int {
if (!hasNext()) throw NoSuchElementException()
return current++
}
}
}
// 使用
val customSeq = RangeSequence(1, 5)
println(customSeq.toList()) // [1, 2, 3, 4, 5]
注意事项和最佳实践
何时使用序列:
- 大数据集(避免中间集合)
- 复杂操作链(多个map/filter操作)
- 需要惰性求值(按需计算)
- 无限数据流(如生成器模式)
何时使用集合:
- 小数据集(性能差异不大)
- 需要多次访问(序列每次计算都重新遍历)
- 需要索引访问(序列不支持直接索引)
- 简单操作(代码更清晰)
重要提示:
val seq = (1..5).asSequence()
// 错误:每次末端操作都会重新计算
val list1 = seq.toList() // 计算一次
val list2 = seq.toList() // 重新计算
// 正确:缓存结果
val cached = seq.toList() // 计算并缓存
val use1 = cached
val use2 = cached
实战案例
案例1:搜索引擎建议
fun searchSuggestions(
query: String,
dictionary: Sequence<String>
): List<String> {
return dictionary
.filter { it.startsWith(query, ignoreCase = true) }
.sortedBy { it.length } // 短词优先
.take(10) // 只取前10个
.toList()
}
案例2:数据处理管道
fun processSensorData(data: Sequence<SensorReading>): AnalysisResult {
return data
.filter { it.quality == Quality.GOOD }
.windowed(5, 1) // 5个数据的滑动窗口
.map { window ->
val avg = window.map { it.value }.average()
val stdDev = calculateStdDev(window.map { it.value })
WindowAnalysis(avg, stdDev)
}
.filter { it.stdDev < 1.0 } // 过滤稳定窗口
.fold(AnalysisResult()) { acc, window ->
acc.add(window)
}
}
序列是 Kotlin 中强大的功能,特别适合函数式编程和数据处理场景。掌握序列可以帮助你写出更高效、更优雅的代码。