2019-11-14:谈谈Kotlin中的Sequence,为什么它处理集合操作更加高效?
Moosphan opened this issue · comments
Hulk Su commented
2019-11-14:谈谈Kotlin中的Sequence,为什么它处理集合操作更加高效?
DQ commented
listOf(1, 2, 3, 4)
.asSequence()
.map { it * it }
.find { it > 3 }
// 结果: 4
如图:
List 处理数据时, 每一个操作, 都会应用到所有元素, 且 生成新的 List 并继续下一步操作; (感觉是横向的)
Sequence 处理数据时, 针对每一个元素, 执行所有操作流, 直接得出单个元素的最终结果; (感觉是纵向的)
结论 Sequence 高效的原因在于:
- 每一步操作之间不会产生临时数据
- 由于可以直接得到单个元素的最终结果, 所以减少运算次数, 如上图, map 只进行了 2 次
补充:
也有例外, 当只有一次操作时, 比如 只 进行 filter or average or sum ...等, List 的效率是要高于 Sequence 的
乱 commented
集合操作低效在哪?
处理集合时性能损耗的最大原因是循环。集合元素迭代的次数越少性能越好。
我们写个例子:
list
.map { it ++ }
.filter { it % 2 == 0 }
.count { it < 3 }
反编译一下,你会发现:Kotlin编译器会创建三个while循环
。
Sequences 减少了循环次数
Sequences
提高性能的秘密在于这三个操作可以共享同一个迭代器(iterator),只需要一次循环即可完成。Sequences
允许 map 转换一个元素后,立马将这个元素传递给 filter操作 ,而不是像集合(lists) 那样,等待所有的元素都循环完成了map操作后,用一个新的集合存储起来,然后又遍历循环从新的集合取出元素完成filter操作。
Sequences 是懒惰的
上面的代码示例,map
、filter
、count
都是属于中间操作,只有等待到一个终端操作,如打印、sum()
、average()
、first()
时才会开始工作,不信?你跑下下面的代码?
val list = listOf(1, 2, 3, 4, 5, 6)
val result = list.asSequence()
.map{ println("--map"); it * 2 }
.filter { println("--filter");it % 3 == 0 }
println("go~")
println(result.average())
扩展:Java8 的 Stream(流) 怎么样呢?
list.asSequence()
.filter { it < 0}
.map { it++ }
.average()
list.stream()
.filter { it < 0}
.map { it++ }
.average()
stream
的处理效率几乎和Sequences
一样高。它们也都是基于惰性求值的原理并且在最后(终端)处理集合。