翻译说明:函数
原标题: Kotlin : Slow List and Lazy Sequencepost
原文地址: medium.com/@elye.proje…性能
原文做者: Elyespa
自从Kotlin能够兼容Java7上后,很高兴的是咱们能够轻松地在List上使用各类集合操做符而且能够链式调用它们。可是咱们知道在某些状况下使用List的迭代器并非最好的方式,那么还有另外一种方式就是使用序列(sequence)翻译
在咱们了解序列在某些状况下为何更好以前,让我告诉你一些关于List的内容。日志
List内部使用Iterator进行操做。这是一个很是勤奋的群体,我链式调用它的每个操做,它都能确保没有任何遗漏的完成。code
val list = listOf(1, 2, 3, 4, 5, 6)
list.map{ it * 2 }.filter { it % 3 == 0 }.average()
复制代码
正如你在上面的插图中看到的,对于每一步操做,List的每一个元素都须要被处理。cdn
为了证实这一点,让咱们输出一些log日志:blog
val list = listOf(1, 2, 3, 4, 5, 6)
val result = list
.map{ println("In Map"); it * 2 }
.filter { println("In Filter");it % 3 == 0 }
println("Before Average")
println(result.average())
复制代码
结果以下:开发
In Map
In Map
In Map
In Map
In Map
In Map
In Filter
In Filter
In Filter
In Filter
In Filter
In Filter
Before Average
9.0
复制代码
很棒。勤奋努力地工做,并完成全部的过程。
好的,如今让咱们经过调用asSequence()扩展函数来将List转化成一个序列(Sequence)。
val list = listOf(1, 2, 3, 4, 5, 6)
val result = list.asSequence()
.map{ println("In Map"); it * 2 }
.filter { println("In Filter");it % 3 == 0 }
println("Before Average")
println(result.average())
复制代码
结果以下:
Before Average
In Map
In Filter
In Map
In Filter
In Map
In Filter
In Map
In Filter
In Map
In Filter
In Map
In Filter
9.0
复制代码
哇,有趣...,注意到 "Before Average" 是最早输出的,换句话说,若是我不调用 average() 函数,那么序列(sequence)就没有作任何操做。
它很懒,不想作任何工做,直到终端链接到它。终端就像是一种操做,实际上就是一个操做符扩展函数,会返回其余类型结果(除了Sequence<T>以外),例如 sum(),average(),first()等...。甚至toList()用于将Sequence转换为List
除此以外,你会注意到它输出的In Map和In Filter交叉出现。这意味着它会在经过链条以前一个接一个地经过链条,直到它经过终端,即平均操做,而后经过下一个元素。
若是你这样想,想象你想要拿到集合变换后的第一个元素。
val list = listOf(1, 2, 3, 4, 5, 6)
val result = list
.map{ println("In Map $it"); it * 2 }
.filter { println("In Filter $it");it % 3 == 0 }
println(result.first())
复制代码
结果以下:
In Map 1
In Map 2
In Map 3
In Map 4
In Map 5
In Map 6
In Filter 2
In Filter 4
In Filter 6
In Filter 8
In Filter 10
In Filter 12
6
复制代码
全部在一块儿总共13行,这意味着13次操做。
val sequence = sequenceOf(1, 2, 3, 4, 5, 6)
val result = sequence
.map{ println("In Map $it"); it * 2 }
.filter { println("In Filter $it");it % 3 == 0 }
println(result.first())
复制代码
结果是:
In Filter 2
In Map 2
In Filter 4
In Map 3
In Filter 6
6
复制代码
仅仅7行即7次操做。这意味着它只要找到第一个元素的那一刻,就会终止整个过程。
你能够想像,这会加快整个运行的过程。
让咱们作一些试验。
val sequence = generateSequence(1) { it + 1 }.take(50000000)
val list = sequence.toList()
println("List Map Sum= "
+ measureNanoTime { list.map { it * 2 }.sum() })
println("Sequence Map Sum "
+ measureNanoTime { sequence.map { it * 2 }.sum() })
println("List Map Average "
+ measureNanoTime { list.map { it * 2 }.average() })
println("Sequence Map Average "
+ measureNanoTime { sequence.map { it * 2 }.average() })
复制代码
结果是:
List Map Sum 14727907362
Sequence Map Sum 2074397969
List Map Average 11460520785
Sequence Map Average 3268960487
复制代码
看上去像前面的有一个Map操做时,Sequence的性能会比List更快。也许它不须要像List那样存储map操做后的中间结果,从而会更快。
val sequence = generateSequence(1) { it + 1 }.take(50000000)
val list = sequence.toList()
println("List Filter Sum "
+ measureNanoTime { list.filter { it % 3 == 0 }.sum() })
println("Sequence Filter Sum "
+ measureNanoTime { sequence.filter { it % 3 == 0 }.sum() })
println("List Filter Average "
+ measureNanoTime { list.filter { it % 3 == 0 }.average() })
println("Sequence Filter Average "
+ measureNanoTime { sequence.filter { it % 3 == 0 }.average() })
复制代码
结果是:
List Filter Sum 506351694
Sequence Filter Sum 873175271
List Filter Average 391790033
Sequence Filter Average 838510968
复制代码
对于前面的Filter操做,Sequence比List更慢。 深刻了解函数,看起来像Sequence的Filter操做须要有更多的开销来检查某些状态,而List的Filter则是一个简单的检查并收集新的元素。
val sequence = generateSequence(1) { it + 1 }.take(50000000)
val list = sequence.toList()
println("List Map Filter Sum\t\t " + measureNanoTime {
list.map { it * 2 }.filter { it % 3 == 0 }.sum() })
println("Sequence Map Filter Sum\t " + measureNanoTime {
sequence.map { it * 2 }.filter { it % 3 == 0 }.sum() })
println("List Map Filter Average\t\t " + measureNanoTime {
list.map { it * 2 }.filter { it % 3 == 0 }.average() })
println("Sequence Map Filter Average\t " + measureNanoTime {
sequence.map { it * 2 }.filter { it % 3 == 0 }.average() })
复制代码
结果是:
List Map Filter Sum 34845242323
Sequence Map Filter Sum 2820436086
List Map Filter Average 2328258876
Sequence Map Filter Average 18618444560
复制代码
一个相对使人惊讶的结果,如Map:Filter:Sum,Sequence比List快得多,而Map:Filter:Average,List比Sequence要快得多。
val sequence = generateSequence(1) { it + 1 }.take(50000000)
val list = sequence.toList()
println("List Sum " + measureNanoTime { list.sum() })
println("Sequence Sum " + measureNanoTime { sequence.sum() })
println("List Average " + measureNanoTime { list.average() })
println("Sequence Average " + measureNanoTime { sequence.average() })
复制代码
结果是:
List Sum 91726022
Sequence Sum 592771887
List Average 101141460
Sequence Average 622616340
复制代码
没有任何中间操做,明显列表List比序列Sequence要快。
首先,说下为何要翻译这篇博客?关于Kotlin中的Sequence和List的使用以及源码解析相关的文章我已经写过两篇了,这篇博客主要吸引个人一点就是以更多运行的例子试验和相关幽默的配图更加形象地描述了Sequence,List的区别以及各自的使用场景。
然而,这篇博客并无深刻源码去讲解Sequence的实现,这篇以前写的博客 浅谈Kotlin中的序列(Sequences)源码彻底解析(十) 从源码角度带你一步步分析Sequence序列背后的原理,关于如何正确使用Sequence和List以及各自使用场景,以前翻译的一篇博客 [译]Kotlin中是应该使用序列(Sequences)仍是集合(Lists)? 会有更加全面的介绍。
最后,有了这三篇文章应该更加全面理解了Sequence的原理和使用。
欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不按期翻译一篇Kotlin国外技术文章。若是你也喜欢Kotlin,欢迎加入咱们~~~