翻译说明:

原标题: Kotlin : Slow List and Lazy Sequence

原文地址: https://medium.com/@elye.project/kotlin-slow-list-and-lazy-sequence-61691fc974c5

原文作者: Elye

自从Kotlin可以兼容Java7上后,很高兴的是我们可以轻松地在List上使用各种集合操作符并且可以链式调用它们。但是我们知道在某些情况下使用List的迭代器并不是最好的方式,那么还有另一种方式就是使用序列(sequence)

没有背景只能辛苦工作的List列表

在我们了解序列在某些情况下为什么更好之前,让我告诉你一些关于List的内容。

List内部使用Iterator进行操作。这是一个非常勤奋的群体,我链式调用它的每一个操作,它都能确保没有任何遗漏的完成。

val list = listOf(1, 2, 3, 4, 5, 6)
list.map{ it * 2 }.filter { it % 3 == 0 }.average()

正如你在上面的插图中看到的,对于每一步操作,List的每个元素都需要被处理。

为了证明这一点,让我们输出一些log日志:

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

很棒。勤奋努力地工作,并完成所有的过程。

懒惰的家伙,Sequence序列…

好的,现在让我们通过调用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\

那么,序列Sequence到底有什么好处呢?

如果你这样想,想象你想要拿到集合变换后的第一个元素。

让我们看下List处理方式:

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())
  • 1
  • 2
  • 3
  • 4
  • 5

结果如下:

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次操作。

让我们看下Sequence处理方式:

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次操作。这意味着它只要找到第一个元素的那一刻,就会终止整个过程。

你可以想像,这会加快整个运行的过程。

加速仅仅只适用于first()操作吗?

让我们做一些试验。

试验Map操作

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
  • List: 在Map:Sum操作上花费了14.7s,在Map:Average操作上花费了11.5s
  • Sequence: 在Map:Sum操作上花费了2.1s, 在Map:Average操作上花费了3.3s

看上去像前面的有一个Map操作时,Sequence的性能会比List更快。也许它不需要像List那样存储map操作后的中间结果,从而会更快。

试验Filter操作

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
  • List: 在Filter:Sum操作上花费了0.5s,在Filter:Average操作上花费了0.4s
  • Sequence: 在Filter:Sum操作上花费了0.9s, 在Filter:Average操作上花费了0.8s

对于前面的Filter操作,Sequence比List更慢。 深入了解函数,看起来像Sequence的Filter操作需要有更多的开销来检查某些状态,而List的Filter则是一个简单的检查并收集新的元素。

试验Map和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
  • List: 在Map:Filter:Sum操作上花费了34.8s,在Map:Filter:Average操作上花费了2.3s
  • Sequence: 在Map:Filter:Sum操作上花费了2.8s, 在Map:Filter:Average操作上花费了18.6s

一个相对令人惊讶的结果,如Map:Filter:Sum,Sequence比List快得多,而Map:Filter:Average,List比Sequence要快得多。

试验直接使用Sequence和List

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: 在Sum操作上花费了0.1s,在Average操作上花费了0.1s
  • Sequence: 在Sum操作上花费了0.5s, 在Average操作上花费了0.6s

没有任何中间操作,明显列表List比序列Sequence要快。

总结:

  • 1、当不需要中间操作时,使用List
  • 2、当仅仅只有map操作时,使用sequence
  • 3、当仅仅只有filter操作时,使用List
  • 4、如果末端操作是first时,使用sequence
  • 5、对于没有提及的其他操作符或者其他操作符的组合,请尝试使用例子去验证一下

译者有话说:

首先,说下为什么要翻译这篇博客?关于Kotlin中的Sequence和List的使用以及源码解析相关的文章我已经写过两篇了,这篇博客主要吸引我的一点就是以更多运行的例子试验和相关幽默的配图更加形象地描述了Sequence,List的区别以及各自的使用场景。

然而,这篇博客并没有深入源码去讲解Sequence的实现,这篇之前写的博客 浅谈Kotlin中的序列(Sequences)源码完全解析(十) 从源码角度带你一步步分析Sequence序列背后的原理,关于如何正确使用Sequence和List以及各自使用场景,之前翻译的一篇博客 [译]Kotlin中是应该使用序列(Sequences)还是集合(Lists)? 会有更加全面的介绍。

最后,有了这三篇文章应该更加全面理解了Sequence的原理和使用。

Sequence 加速的更多相关文章

  1. 【 CodeForces - 392C】 Yet Another Number Sequence (二项式展开+矩阵加速)

    Yet Another Number Sequence Description Everyone knows what the Fibonacci sequence is. This sequence ...

  2. POJ 2778 DNA Sequence(AC自动机+矩阵加速)

    DNA Sequence Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9899   Accepted: 3717 Desc ...

  3. HDU 5950 - Recursive sequence - [矩阵快速幂加速递推][2016ACM/ICPC亚洲区沈阳站 Problem C]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5950 Farmer John likes to play mathematics games with ...

  4. HDU5950 Recursive sequence (矩阵快速幂加速递推) (2016ACM/ICPC亚洲赛区沈阳站 Problem C)

    题目链接:传送门 题目: Recursive sequence Time Limit: / MS (Java/Others) Memory Limit: / K (Java/Others) Total ...

  5. jzoj6008. 【THUWC2019模拟2019.1.18】Sequence (矩阵加速)

    题面 茉优最近研究发现,一个人的想愿能力可以认为是字符串S的一个子串S[l,r],而连接值可以认为是这个子串的本质不同子序列个数.现在她想验证她的结论是否正确,于是她给了你Q个询问,希望你帮她来计算, ...

  6. 【距离GDOI:128天】【POJ2778】DNA Sequence(AC自动机+矩阵加速)

    已经128天了?怎么觉得上次倒计时150天的日子还很近啊 ....好吧为了把AC自动机搞透我也是蛮拼的..把1030和这道题对比了无数遍...最终结论是...无视时间复杂度,1030可以用这种写法解. ...

  7. cocos2d-x 动画加速与减速

    转自:http://novacreo.com/%E7%A8%8B%E5%BA%8F%E7%BB%84/cocos2d-x%E5%8A%A8%E7%94%BB%E5%8A%A0%E9%80%9F%E4% ...

  8. hdu_5950_Recursive sequence(矩阵快速幂)

    题目链接:hdu_5950_Recursive sequence 题意:递推求解:F(n) = 2*F(n-2) + F(n-1) + n4 和F(1) = a,F(2) = b: 题解: 一看数据范 ...

  9. 【论文阅读】Sequence to Sequence Learning with Neural Network

    Sequence to Sequence Learning with NN <基于神经网络的序列到序列学习>原文google scholar下载. @author: Ilya Sutske ...

随机推荐

  1. luogu Eat the Trees

    /* 用和模板类似的方法就行 但是实际上弱化版不用考虑匹配情况限制更加宽松, 只需要保存每个位置有无插头即可, */ #include<cstdio> #include<algori ...

  2. hive计算周一的日期

    ) FreeMarker --',-7)?date('yyyy-MM-dd'),'week')?string('yyyy-MM-dd')}'

  3. switch嵌套--猜拳游戏

    <!DOCTYPE html> <html>     <head>         <meta charset="UTF-8">   ...

  4. 安全测试6_Web安全工具第三节(Web安全工具)

    之前学习了下安全工具前的一些基础知识,今天来学习下安全工具,这些工具是python写的. 1.敏感文件探测入门: 敏感文件探测的原理如下: 一般探测敏感文件我们都是借助工具扫描,这里我给大家推荐一个简 ...

  5. 零基础学习python_魔法方法(41-48课)(迭代器)

    接下来这个为啥要叫魔法方法呢,额,这个嘛我是跟小甲鱼的视频取的名字一样的,因为会讲比较多杂的东西,有... 魔法方法详细阅读地址:http://bbs.fishc.com/thread-48793-1 ...

  6. Linux NTP

    1.Server 2.QuickStart last 1.Server 0.cn.pool.ntp.org 1.cn.pool.ntp.org 2.cn.pool.ntp.org 3.cn.pool. ...

  7. 如何安装 Microsoft Office 兼容包,以便您可以在早期版本的 Microsoft Office 中打开和保存 Office Open XML 格式

    https://support.microsoft.com/zh-cn/kb/923505 针对 Office 2003 的支持已终止 Microsoft 已于 2014 年 4 月 8 日终止了针对 ...

  8. 8.2.优化SQL语句

    8.2.优化SQL语句 数据库应用程序核心操作逻辑都是通过执行SQL语句来执行,不管是直接通过解释器还是通过后台API提交. 调优手册里面的这一节内容帮助各种各样MySQL程序加快速度.手册包括SQL ...

  9. PHP企业微信配置点击事件。

    1. 2. 3.URL接受值进行签名验证. <?phpinclude_once "../commmm/WXBizMsgCrypt.php";/*微信提供 demo*/// 假 ...

  10. opencv画出轮廓外接矩形

    Mat cannyImage; /// Detect edges using canny Canny(src, cannyImage, , , ); vector<vector<Point ...