美文网首页Android开发经验谈程序员Android开发
Kotlin 性能优化利器 —— Sqeuence 原理浅析

Kotlin 性能优化利器 —— Sqeuence 原理浅析

作者: df556ada620a | 来源:发表于2019-05-31 21:09 被阅读21次

    作者:0xCAFEBOY
    https://juejin.im/post/5ce91a556fb9a07f050a4484#comment

    前言

    本文将介绍 Kotlin 中 序列(Sequence)的概念及使用,并介绍该惰性集合操作对集合链式调用性能优化背后的原理。

    阅读本文大概需要 5 分钟,写作本文大概消耗 7 个小时

    目录

    序列(Sequence)

    概念

    在使用 Kotlin 集合操作符进行链式调用时,例如 mapfilter 时,都会在函数内部创建中间集合,比如下面的例子,使用 map 和 filter 在 User 集合中筛选出性别为男的成员,返回结果是一个集合。

    users.map(User :: sex)
    

    序列的用法

    序列的用法很简单,只需要再集合后添加asSeqence() 函数即可

    users.asSequence()     .map(User :: sex)     .filter {it.sex.equals("male")}
    

    这里插播一个概念,其中 User :: user 是成员引用,具体介绍如下

    成员引用(Member References)

    概念

    成员引用可以使你方便的调用某个类的成员,这个成员包括对应类的属性或方法.双冒号前的是被引用的类,双冒号后是需要返回的属性或方法名,如下所示是返回 User 成员的 sex 属性:

    User :: sex
    

    成员引用可以方便的赋值给其他变量或函数,例如上述寻找性别为 male 的例子,也可以用稍微复杂的写法,如下:

    users.map(user : User -> user.sex)     .filter {it.sex.equals("male")}
    

    可见成员引用的写法可读性更强。

    再谈序列

    让我们回到序列介绍。上文提到使用 map 和 filter 时,都会在函数内部创建中间集合,这会导致一个问题,如果源列表,就是 users 中元素特别多,集合的链式处理会变得十分低效,原因是创建了多次中间集合。而如果先将待处理集合通过 asSequence() 方法转换为序列,再进行 map 和 filter 操作,就会变得十分高效。对于是否使用序列进行集合操作,有几个前提,如果使用不当,反而会造成性能损失。这里总结一下使用场景:

    序列性能测试

    上文提到,是否使用序列的条件之一是处理大量数据,那么这个阈值究竟是多少?下面来进行一个性能测试,构造一个商品列表,其中每个商品包含商品名和价格两个属性,现在要求出对每个商品的价格加价 100 后,价格为奇数 的商品的个数,这里用到了 count() 方法,作用是求得集合内满足 count 条件的元素的个数,代码如下:

    /** * 商品类 */data class Commodity(var name: String, var price: String)
    
    import java.util.*fun main(args: Array<String>) {    val commodityList = ArrayList<Commodity>()    for (i in 0..1000000) {        val goods = Commodity("商品 $i", i * 5)        commodityList.add(goods)    }    val startTime = System.currentTimeMillis()    commodityList            .asSequence() // 使用此函数代表使用 Kotlin 序列功能            .map { it.price + 100 }            .count { it % 2 != 0 }    println("consume time is ${System.currentTimeMillis() - startTime} ms")}
    

    测试结果折线图如下,其中横坐标为集合内元素的个数,纵坐标为代码执行时间,橙色线代表未使用序列,蓝色线代表使用序列:

    表格对比如下:

    由图可得出如下结论:

    • 上文提到的阈值大致为「一百万」个元素,大于该阈值时,使用序列大致能带来 90 % 的性能提升

    • 在小于「十万」个元素时,使用序列反而会造成性能下降

    为什么序列会提高集合操作的性能?

    1. 序列对集合的操作是惰性的。

    2. 不需要额外的创建中间集合保存链式操作的中间结果

    对于第一点,惰性这个词可能给人带来迷惑,这和使用序列后,集合的计算方式有关,下面来介绍这一点,在下面这个例子中,你需要在一个整型数据集合,将每个数乘以 2 ,并找出集合中小于 10 的第一个元素。

    var result = listOf(2,4,6,8,10).asSequence                  .map(it * 2)                  .find(it > 10)
    

    find() 方法的作用是在集合中查找满足条件的第一个元素,并返回查找到的值。

    下图是 Kotlin 使用序列进行处理

    由图可知,所谓惰性,就是在使用 mapfilter 等操作符的时候,在代码的执行顺序上,不会优先遍历所有的集合,即偷个懒,先对集合中第一个元素进行 map 和 filter, 然后对该元素执行 find 操作,发现满足 find 的条件,就立刻返回结果,由此可见,在有 map 和 find 或 last 等操作符组合时,序列的性能优化能发生最大的作用。

    小结

    1. 在进行集合操作时,使用序列操作符,可以降低集合操作的时间和占用的空间,降低时间的原因是惰性操作,降低空间占用的原因是序列在执行操作时不会创建中间集合。

    2. 序列操作虽好,但也要视业务场景决定是否使用,否则反而会降低效率。

    参考文献

    • 《实战 Kotlin 》

    • Kotlin系列之序列(Sequences)源码完全解析

    最后关于作者

    作者目前在深圳,13年java转Android开发,在小厂待过,也去过华为,OPPO等,去年四月份进了腾讯一直到现在。等大厂待过也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

    我花了一年时间整理出一份腾讯T4级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

    主要包括腾讯,以及字节跳动,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。至于能学会多少,真的只能看你自己

    全套体系化高级架构视频;七大主流技术模块

    部分展示;java内核视频+源码+笔记

    image

    免费分享

    点击获取资料文档;

    《腾讯T4级别Android架构师技术脑图+全套视频》​link.juejin.im

    为什么免费分享?

    我不想有很多开发者朋友因为门槛而错过这套高级架构资料,错过提升成为架构师的可能。国内程序员千千万,大多数是温水煮青蛙的现状,靠着天天加班,拿着外人以为还不错的薪资待遇。

    请记住自身技术水平才是我们的核心竞争力,千万别把年轻和能加班当做本钱。

    相关文章

      网友评论

        本文标题:Kotlin 性能优化利器 —— Sqeuence 原理浅析

        本文链接:https://www.haomeiwen.com/subject/dkxntctx.html