禅与计算机程序设计艺术Kotlin编程Kotlin开发指南

Kotlin入门笔记八:Lambda和序列

2018-10-18  本文已影响31人  Taonce
kotlin

序列(Sequence):

首先我们来了解一下什么是序列,序列其实类似集合的一个接口,只不过它的操作都是惰性集合操作,所有在集合上的操作符都适用于序列。下面我们来看看如何生成一个序列:

// 列表和序列
val list = listOf(1,2,3,4)
println(list)
println(list.asSequence())

输出:
[1, 2, 3, 4]
kotlin.collections.CollectionsKt___CollectionsKt$asSequence$$inlined$Sequence$1@13fee20c

生成一个序列的一种方式就是通过list.asSequence(),但是我么在输出日志中才发现它的输出其实没有内容,那么如果要查看序列的内容可以将它转换为集合。

println(list.asSequence().toList())

输出:
[1, 2, 3, 4]

这样我们接可以看到结果。


序列的优势:

那么有了集合这么好用的东西,我们为什么要使用序列呢?接下来我们看一个小小的例子:
我们拿到了100W条用户数据,需要将这些用户年龄是偶数的姓名全打印出来。如果不使用序列,我们的做法为:

println(userList
        .filter { it.age % 2 != 0 }
        .map { User::age })

这个写法肯定没有错,对于少量数据来说也没什么影响,可是这里大家注意,是100W条数据,我们可以点击mapfilter查看源码,每一步操作都会生成另外一个集合,对于大量数据来说,这可是一笔很大的消耗,在性能上是很不理想的。这时候就到了我们序列大显身手的时候了,来看看序列是如何减少性能消耗的:

println(userList.asSequence()
        .filter { it.age % 2 != 0 }
        .map { User::age }
        .toList())

这里我们先将集合转换为序列,在最后的操作中才将序列转换为集合的。序列在中间操作都是惰性的,不会创建额外的集合来保存过程中产生的中间结果,使用序列可以高效的对集合元素执行链式操作。


序列操作的执行过程:

序列操作.png

序列操作分为两个过程:中间操作、末端操作。中间操作全部都是惰性操作,如果没有执行末端操作,中间操作都会被延期。我们来看以下代码加深理解:

val list = listOf(1, 2, 3, 4)
list.asSequence().filter { print(it);it > 2 }
list.asSequence().filter { print(it);it > 2 }.toList()

我们在中间操作filter()打印集合的元素,但是在日志中会发现,不加toList()的是不会有任何结果的,只有加上了toList()才会执行print(it)操作,也就证明了为什么说中间操作是惰性的,在没有末端操作的时候,中间操作会被延期。

注意:末端操作的定义可以理解为:只要不是在这个操作后生成的对象依旧是序列就属于末端操作。


序列和集合的执行顺序:

对于一个链式操作,我们可以先大致猜一下,序列和集合的执行顺序是否是一样?
我们来通过一个简单的例子理解下:

    val list = listOf(1, 2, 3, 4)
    list.map { print("map($it) "); it * it }
            .filter { print("filter($it )"); it > 5 }
    println()
    list.asSequence()
            .map { print("map($it) ");it * it }
            .filter { print("filter($it) ");it > 5 }
            .toList()

这个例子很简单,map之后filter,每一步操作我们都做输出处理,结果如下:

map(1) map(2) map(3) map(4) filter(1 )filter(4 )filter(9 )filter(16 )
map(1) filter(1) map(2) filter(4) map(3) filter(9) map(4) filter(16) 

从结果我们一眼就能看出,集合和序列在链式的执行顺序是不一样的,集合在链式中先处理完第一步所有的元素,再处理第二步所有的元素,以此类推。而序列不是,序列是对每个元素做链式操作,只有第一个元素执行完所有的链式操作,才执行第二个元素的链式操作。

千万不要小瞧这个区别,它将为我们在大量数据处理上带来很大的优化和便捷。比如:

    val list = listOf(1, 2, 3, 4,·······)
    list.map { it * it }
            .find { it > 5 }
    list.asSequence()
            .map { it * it }
            .find { it > 5 }

这里我们想象集合中有大量的数据,集合和序列的执行流程如下:

序列和集合的执行顺序.png
从图中我们可以得出这样一个结论,在map()中间操作的时候,集合需要将每一个元素都执行一次,但是序列在找到满足find { it > 5 }的元素时,map()操作将不会继续执行,可以减少不需要的大量操作。这就是序列的另一大优势。

总结:

无论是在集合还是序列的链式操作中,都需要大量的使用到Lambda,熟练的使用Lambda结合集合和序列的转换将在日常开发中得到一种很完美的编码体验,希望大家一定要熟悉的理解序列和集合之间联系和区别。

下一份笔记将为大家带来Kotlin版的MVP+OKhttp3+Retrofit+RxJava的项目基本框架


写在最后

每个人不是天生就强大,你若不努力,如何证明自己,加油!

Thank You!

--Taonce

上一篇 下一篇

猜你喜欢

热点阅读