Kotlin 中的 Collections and Sequences 有什么不同?

1,002 阅读4分钟

在我们日常处理数据时, Kotlin 中 Collections(集合)是非常好用的数据结构,同时 Kotlin 的标准库中也实现了非常多的扩展方法,帮助我们更好的使用 Collections。你也许发现了还有另一种数据结构 Sequences (序列),它同样也可以实现和 Collections 一样的功能,那么这两种数据结构有什么区别呢?以及我们何时该使用 Collections,何时该使用 Sequences 呢?今天这篇文章就告诉你。

Collections vs Sequences

  1. 最大的不同在于:在每一次数据转化时,Collections 是立即执行的,而 Sequences 是延期执行的。

例如,我们有一个用户集合,需要找出所有 name 是以 “w“ 开头的用户,并返回这些用户的名字集合。那么我们可以使用 map 和 filter 这两个操作符来实现我们的需求。

下面是分别使用 Collections 和 Sequences 的两种实现方式,可以看到几乎一样,Sequences 只是在开始操作之前做了 asSequence() 的操作。

val uses = arrayListOf(User("1", "wanbo"), User("2", "zhangsan"), User("3", "lisi"))
val result1 = uses.map(User::name).filter { it.startsWith("w") }.toList()
val result2 = uses.asSequence().map(User::name).filter { it.startsWith("w") }.toList()

我们可以分别打印下面这两句代码,然后找出它们的区别所在。

println(uses.map(User::name))
println(uses.asSequence().map(User::name))
    
// 打印结果
[wanbo, zhangsan, lisi]
kotlin.sequences.TransformingSequence@506e1b77

可以看出,Collections 在执行 map 操作后,返回了一个新的集合,而 Sequences 返回的是一个 新的 Sequence 对象。

结合 Collections 和 Sequences 的定义,我们可以得出以下结论。

Collections:每一次数据操作都是立即执行的,它会把每一步操作后的结果,存储在一个新的临时集合中,然后使用这个新集合继续下一步数据操作。

Sequences:每一次数据操作都是延期执行的,它会把每一步变换数据的操作存储在新的 Sequence 对象中,传递给下一个操作。Sequences 有两种操作:中间操作和末端操作,例如 map 和 filter 都属于中间操作,而 toList 属于末端操作。中间操作返回的是一个存储变换数据操作新序列,而末端操作返回的是一个结果。

所以当我们打印上面的 map 操作时,Sequences 返回的是一个新的 Sequence 对象,也只有当 toList 这个末端操作执行是,才会触发所有延期执行的计算操作。

那么这么做有什么好处吗?

上面👆的例子中只有三条数据,如果我们有一百万条数据,使用 Collections 就会变得十分低效,会造成不必要的内存开销。

2. 数据操作执行顺序的不同

Collections 是将全部数据执行 map 操作,然后返回新的集合去执行 filter 操作。

Sequences 是按顺序将所有操作应用到每一条数据上,即:处理完第一条数据(先 map 后 filter),然后再处理第二条数据,以此类推。

例如,我们还是上面的数据,需求然后找出第一个 name 以 “w” 开头的用户名(不区分大小写w)。这样的话,我们可以使用 map 和 first 来实现需求。

val uses = arrayListOf(User("1", "wanbo"), User("2", "zhangsan"), User("3", "lisi"))
val result1 = uses.map { it.name.toUpperCase() }.first { it.startsWith("W") }
val result2 = uses.asSequence().map { it.name.toUpperCase() }.first { it.startsWith("W") }

通过上面的结论我们可知,Collections 会把所有 name 变成大写,然后执行 first 操作,而 Sequences 会按数据顺序执行,正巧第一个数据就满足,那么当处理完第一条数据的时候,就直接可以返回结果了,不需要继续执行后续操作了。

同样在拥有很多条数据的时候,Sequences 在执行效率上的表现也是优于 Collections 的。

如何选择?

很简单,根据数据大小来选择,如果是轻量级的数据,则使用 Collections,如果是大量级的数据,那么就使用 Sequences。

另外,涉及到数据的操作,选取合适的数据结构,选用合适的策略都是非常重要的,例如我们第一个例子,如果是先 filter 后 map 的话,在性能上会有更好的表现,你发现了吗?🤣,所以在写代码之前要多思考,多动脑,才能写出优良的代码。

欢迎关注同名微信公众号【Android丨Kotlin】