自定义Kotlin zipAll函数触发OutOfMemoryError的原因及解决方法
自定义Kotlin zipAll函数触发OutOfMemoryError的原因及解决方法
嗨,我来帮你排查这个内存溢出的问题~
你写的zipAll函数之所以会抛出OutOfMemoryError,核心问题出在每次循环都为序列创建新的迭代器,导致循环永远不会终止,一直在生成重复的元素,最终撑爆内存。具体来说:
- 你在
while循环的判断条件里写了seq.all { it.iterator().hasNext() }:每次调用it.iterator()都会生成一个全新的迭代器,这个迭代器永远指向序列的开头,所以hasNext()永远返回true,循环会无限执行下去。 - 同样,
seq.map { it.take(1).first() }也是每次从新迭代器里取第一个元素,所以每次yield的都是各序列的第一个元素,相当于一直在生成[1,3]这样的列表,toList()会不断把这些列表存入内存,最终导致堆空间耗尽。
正确的实现方式
要修复这个问题,我们需要为每个序列创建唯一的迭代器并复用它,这样每次循环才能真正消耗序列里的元素,直到最短的序列耗尽时终止循环。修改后的代码如下:
fun <T> zipAll(vararg seq: Sequence<T>): Sequence<List<T>> { // 先为每个序列创建迭代器并保存,后续循环复用这些迭代器 val iterators = seq.map { it.iterator() } return sequence { // 检查所有迭代器是否还有下一个元素 while (iterators.all { it.hasNext() }) { // 从每个迭代器取出下一个元素,组成列表yield出去 yield(iterators.map { it.next() }) } } } fun main() { val s = sequenceOf(1,2,3) val s2 = sequenceOf(3,4,5,6) println(zipAll(s, s2).toList()) // 输出:[[1, 3], [2, 4], [3, 5]] }
代码说明
- 先一次性获取所有输入序列的迭代器并存储在
iterators列表中,确保整个函数生命周期内每个序列只用一个迭代器。 while循环条件复用这些迭代器的hasNext(),当任意一个迭代器没有元素时,循环终止,符合你想要的“取最短序列长度”的需求。- 每次
yield时调用迭代器的next(),真正消耗序列里的元素,保证每次生成的列表是序列的下一组元素。
这样修改后,代码就能正常运行,输出你预期的并行迭代结果,也不会再出现内存溢出的问题啦~
备注:内容来源于stack exchange,提问作者Felix Dombek
相关产品推荐
相关产品推荐

