广告位联系
返回顶部
分享到

Kotlin的Collection与Sequence操作异同点介绍

Android 来源:互联网 作者:佚名 发布时间:2022-10-23 20:52:18 人浏览
摘要

在Android开发中,集合是我们必备的容器,Kotlin的标准库中提供了很多处理集合的方法,而且还提供了两种基于容器的工作方式:Collection 和 Sequence。 Collection 是我们常见的,而 Sequence

在Android开发中,集合是我们必备的容器,Kotlin的标准库中提供了很多处理集合的方法,而且还提供了两种基于容器的工作方式:Collection 和 Sequence。

Collection 是我们常见的,而 Sequence 确是我们少见的,甚至很多人都没有听说过(是的,说的就是我 ???? ????)

本篇文章就来介绍一下常用的一些集合操作符,以及 Collection 与 Sequence 的区别,到底怎么用!

Collection 的常见操作

Collection 集合,Kotlin的集合类型和Java不一样,Kotlin的集合分为可变(读写)和不可变(只读)类型(lists, sets, maps, etc),可变类型是在不可变类型前面加Mutable,以我们常用的三种集合类型为例:

1

2

3

List<out E> - MutableList<E>

Set<out E> - MutableSet<E>

Map<K, out V> - MutableMap<K, V>

其实他们的区别就是List实现了Collection接口,而MutableList实现的是List和MutableCollection接口。而 MutableCollection 接口实现了Collection 接口,并且在里面添加了add和remove等操作方法。

可变不可变只是为了区分只读和读写的操作,他们的操作符方式都是相同的。

集合的操作符说起来可就太多了

累计

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

//对所有元素求和

list.sum()

//将集合中的每一个元素代入lambda表达式,然后对lambda表达式的返回值求和

list.sumBy {

    it % 2

}

//在一个初始值的基础上,从第一项到最后一项通过一个函数累计所有的元素

list.fold(100) { accumulator, element ->

    accumulator + element / 2

}

//同fold,只是迭代的方向相反

list.foldRight(100) { accumulator, element ->

    accumulator + element / 2

}

//同fold,只是accumulator的初始值就是集合的第一个元素,element从第二个元素开始

list.reduce { accumulator, element ->

    accumulator + element / 2

}

//同reduce但方向相反:accumulator的初始值就是集合的最后一个元素,element从倒数第二个元素开始往前迭代

list.reduceRight { accumulator, element ->

    accumulator + element / 2

}

val list = listOf(1, 2, 3, 4, 5, 6)

//只要集合中的任何一个元素满足条件(使得lambda表达式返回true),any函数就返回true

list.any {

    it >= 0

}

//集合中的全部元素都满足条件(使得lambda表达式返回true),all函数才返回true

list.all {

    it >= 0

}

//若集合中没有元素满足条件(使lambda表达式返回true),则none函数返回true

list.none {

    it < 0

}

//count函数的返回值为:集合中满足条件的元素的总数

list.count {

    it >= 0

}

遍历

1

2

3

4

5

6

7

8

9

//遍历所有元素

list.forEach {

    print(it)

}

//同forEach,只是可以同时拿到元素的索引

list.forEachIndexed { index, value ->

    println("position $index contains a $value")

}

showFields.forEach { (key, value) ->

最大最小

1

2

3

4

5

6

7

8

//返回集合中最大的元素,集合为空(empty)则返回null

list.max()

//返回集合中使得lambda表达式返回值最大的元素,集合为空(empty)则返回null

list.maxBy { it }

//返回集合中最小的元素,集合为空(empty)则返回null

list.min()

//返回集合中使得lambda表达式返回值最小的元素,集合为空(empty)则返回null

list.minBy { it }

过滤(去除)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

//返回一个新List,去除集合的前n个元素

list.drop(2)

//返回一个新List,去除集合的后n个元素

list.dropLast(2)

//返回一个新List,去除集合中满足条件(lambda返回true)的第一个元素

list.dropWhile {

    it > 3

}

//返回一个新List,去除集合中满足条件(lambda返回true)的最后一个元素

list.dropLastWhile {

    it > 3

}

//返回一个新List,包含前面的n个元素

list.take(2)

//返回一个新List,包含最后的n个元素

list.takeLast(2)

//返回一个新List,仅保留集合中满足条件(lambda返回true)的第一个元素

list.takeWhile {

    it>3

}

//返回一个新List,仅保留集合中满足条件(lambda返回true)的最后一个元素

list.takeLastWhile {

    it>3

}

//返回一个新List,仅保留集合中满足条件(lambda返回true)的元素,其他的都去掉

list.filter {

    it > 3

}

//返回一个新List,仅保留集合中不满足条件的元素,其他的都去掉

list.filterNot {

    it > 3

}

//返回一个新List,仅保留集合中的非空元素

list.filterNotNull()

//返回一个新List,仅保留指定索引处的元素

list.slice(listOf(0, 1, 2))

映射

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

//将集合中的每一个元素代入lambda表达式,lambda表达式必须返回一个元素

//map的返回值是所有lambda表达式的返回值所组成的新List

//例如下面的代码和listOf(2,4,6,8,10,12)将产生相同的List

list.map {

    it * 2

}

//将集合中的每一个元素代入lambda表达式,lambda表达式必须返回一个集合

//而flatMap的返回值是所有lambda表达式返回的集合中的元素所组成的新List

//例如下面的代码和listOf(1,2,2,3,3,4,4,5,5,6,6,7)将产生相同的List

list.flatMap {

    listOf(it, it + 1)

}

//和map一样,只是lambda表达式的参数多了一个index

list.mapIndexed { index, it ->

    index * it

}

//和map一样,只不过只有lambda表达式的非空返回值才会被包含在新List中

list.mapNotNull {

    it * 2

}

//根据lambda表达式对集合元素进行分组,返回一个Map

//lambda表达式的返回值就是map中元素的key

//例如下面的代码和mapOf("even" to listOf(2,4,6),"odd" to listOf(1,3,5))将产生相同的map

list.groupBy {

    if (it % 2 == 0) "even" else "odd"

}

元素

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

list.contains(2)

list.elementAt(0)

//返回指定索引处的元素,若索引越界,则返回null

list.elementAtOrNull(10)

//返回指定索引处的元素,若索引越界,则返回lambda表达式的返回值

list.elementAtOrElse(10) { index ->

    index * 2

}

//返回list的第一个元素

list.first()

//返回list中满足条件的第一个元素

list.first {

    it > 1

}

//返回list的第一个元素,list为empty则返回null

list.firstOrNull()

//返回list中满足条件的第一个元素,没有满足条件的则返回null

list.firstOrNull {

    it > 1

}

list.last()

list.last { it > 1 }

list.lastOrNull()

list.lastOrNull { it > 1 }

//返回元素2第一次出现在list中的索引,若不存在则返回-1

list.indexOf(2)

//返回元素2最后一次出现在list中的索引,若不存在则返回-1

list.lastIndexOf(2)

//返回满足条件的第一个元素的索引

list.indexOfFirst {

    it > 2

}

//返回满足条件的最后一个元素的索引

list.indexOfLast {

    it > 2

}

//返回满足条件的唯一元素,如果没有满足条件的元素或满足条件的元素多于一个,则抛出异常

list.single {

    it == 5

}

//返回满足条件的唯一元素,如果没有满足条件的元素或满足条件的元素多于一个,则返回null

list.singleOrNull {

    it == 5

}

排序&逆序

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

val list = listOf(1, 2, 3, 4, 5, 6)

//返回一个颠倒元素顺序的新集合

list.reversed()

/**

 * 返回一个升序排序后的新集合

 */

list.sorted()

//将每个元素代入lambda表达式,根据lambda表达式返回值的大小来对集合进行排序

list.sortedBy {

    it*2

}

/**

 * 功能和上面一样 -> 上面是从小到大排列,这个返回的是从大到小

 */

list.sortedDescending()

list.sortedByDescending {

    it*2

}

/**

 * 根据多个条件排序

 * 先根据age 升序排列,若age相同,根据name升序排列,但是都是默认的升序排列

 */

personList.sortWith(compareBy({ it.age }, { it.name }))

 /**

 * 根据多个条件排序,自定义的规则

 * 构造一个Comparator对象,完成排序逻辑:先按age降序排列,若age相同,则按name升序排列

 */

val c1: Comparator<Person> = Comparator { o1, o2 ->

      if (o2.age == o1.age) {  

          o1.name.compareTo(o2.name)

      } else {

          o2.age - o1.age

      }

 }

personList.sortWith(c1)

  //上面的自定义方式可以通过JavaBean实现Comparable 接口实现自定义的排序

    data class Person(var name: String, var age: Int) : Comparable<Person> {

     override fun compareTo(other: Person): Int {

         if (this.age == other.age) {

              return this.name.compareTo(other.name)

         } else {

             return other.age - this.age

         }

     }

   }

 //sorted 方法返回排序好的list(已有有排序规则的用sorted,不要用sortedby了)

 val sorted = personList.sorted()

Sequence 的常见操作

Sequence 是 Kotlin 中一个新的概念,用来表示一个延迟计算的集合。Sequence 只存储操作过程,并不处理任何元素,直到遇到终端操作符才开始处理元素,我们也可以通过 asSequence 扩展函数,将现有的集合转换为 Sequence ,代码如下所示

1

2

3

4

5

val list = mutableListOf<Person>()

for (i in 1..10000) {

    list.add(Person("name$i", (0..100).random()))

}

list.asSequence()

当我们拿到结果之后我们还能通过toList再转换为集合。

1

list.asSequence().toList()

Sequence的操作符绝大部分都是和 Collection 类似的。常用的一些操作符是可以直接平替使用的。

1

2

3

4

5

6

val list2 = list.asSequence()

  .filter {

      it.age > 50

  }.map {

      it.name

  }.take(3).toList()

居然他们的操作符都长的一样,效果也都一样,导致 Sequence 与 Collection 就很类似,那么既生瑜何生亮!为什么需要这么个东西?既然 Collection 能实现效果为什么还需要 Sequence 呢?他们的区别又是什么呢?

区别与对比

Collection 是立即执行的,每一次中间操作都会立即执行,并且把执行的结果存储到一个容器中,没多一个中间操作符就多一个容器存储结果。

1

2

3

4

5

6

public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {

  return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)

}

public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {

    return filterTo(ArrayList<T>(), predicate)

}

比如常用的 map 和 filter 都是会新建一个 ArrayList 去存储结果,

Sequence 是延迟执行的,它有两种类型,中间操作和末端操作 ,主要的区别是中间操作不会立即执行,它们只是被存储起来,中间操作符会返回另一个Sequence,仅当末端操作被调用时,才会按照顺序在每个元素上执行中间操作,然后执行末端操作。

1

2

3

4

5

6

public fun <T, R> Sequence<T>.map(transform: (T) -> R): Sequence<R> {

    return TransformingSequence(this, transform)

}

public fun <T> Sequence<T>.filter(predicate: (T) -> Boolean): Sequence<T> {

    return FilteringSequence(this, true, predicate)

}

比如常用的 map 和 filter 都是直接返回 Sequence 的this 对象。

1

2

3

4

public inline fun <T> Sequence<T>.first(predicate: (T) -> Boolean): T {

    for (element in this) if (predicate(element)) return element

    throw NoSuchElementException("Sequence contains no element matching the predicate.")

}

然后在末端操作中,会对 Sequence 中的元素进行遍历,直到预置条件匹配为止。

这里我们举一个示例来演示一下:

我们使用同样的筛选与转换,来看看效果

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

val list = mutableListOf<Person>()

for (i in 1..10000) {

    list.add(Person("name$i", (0..100).random()))

}

val time = measureTimeMillis {

    val list1 = list.filter {

        it.age > 50

    }.map {

        it.name

    }.take(3)

    YYLogUtils.w("list1$list1")

}

YYLogUtils.w("耗费的时间$time")

val time2 = measureTimeMillis {

    val list2 = list.asSequence()

        .filter {

            it.age > 50

        }.map {

            it.name

        }.take(3).toList()

    YYLogUtils.w("list2$list2")

}

YYLogUtils.w("耗费的时间2$time2")

运行结果:

当集合数量为10000的时候,执行时间能优秀百分之50左右:

当集合数量为5000的时候,执行时间相差比较接近:

当集合数量为3000的时候,此时的结果就反过来了,Sequence延时执行的优化效果就不如List转换Sequence再转换List了:

总结

Collection 会立即执行对数据的操作,而 Sequence 则是延迟执行,如果数据量比较小,可以使用 Collection ,如果处理的数据量比较大,Sequence 是最好的选择,因为不会创建中间结果集,内存开销更小。

根据上面的测试发现,也不能无脑的使用 Sequence 来优化筛选转换效果,当数据量没有达到一定程度的时候,可能会出现负优化的效果。

当然我们的对象是很简单的对象,我们的筛选也是简单筛选,当对象复杂度到一定程度,筛选条件复杂到一定程度,集合的数量到一定的程度,我们才可以考虑 使用 Sequence 来达到优化效果。

Sequence 的具体的使用还是需要具体场景具体分析来使用,就我们Android应用开发的场景来说,很少会出现这么大的数据量,所以一般来说我们平常开发使用 Collection 即可。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://juejin.cn/post/7155765126627344392
相关文章
  • Flutter加载图片的多样玩法汇总

    Flutter加载图片的多样玩法汇总
    加载本地图片 在项目目录下创建assets文件夹,再在其文件夹下创建images文件夹,后面将需要的图片复制到其中即可 在pubspec.yaml文件中添加引
  • Kotlin的Collection与Sequence操作异同点介绍

    Kotlin的Collection与Sequence操作异同点介绍
    在Android开发中,集合是我们必备的容器,Kotlin的标准库中提供了很多处理集合的方法,而且还提供了两种基于容器的工作方式:Collection 和
  • 实现一个Kotlin函数类型方法

    实现一个Kotlin函数类型方法
    接口与函数类型 业务开发中,经常会有实现一个函数式接口(即接口只有一个方法需要实现)的场景,大家应该都会不假思索的写出如下代
  • Android10 App启动Activity源码分析
    ActivityThread的main方法 让我们把目光聚焦到ActivityThread的main方法上。 ActivityThread的源码路径为/frameworks/base/core/java/android/app/ActivityThread。 1 2
  • Android10客户端事务管理ClientLifecycleManager源码解析

    Android10客户端事务管理ClientLifecycleManager源码解析
    在Android 10 App启动分析之Activity启动篇(二)一文中,简单地介绍了Activity的生命周期管理器是如何调度Activity进入onCreate生命周期的流程。这
  • Kotlin对象的懒加载方式by lazy与lateinit异同介绍

    Kotlin对象的懒加载方式by lazy与lateinit异同介绍
    属性或对象的延时加载是我们相当常用的,一般我们都是使用 lateinit 和 by lazy 来实现。 他们两者都是延时初始化,那么在使用时那么他们两
  • Android类加载流程分析

    Android类加载流程分析
    本文分析的代码基于Android8.1.0源码。 流程分析 从loadClass开始,我们来看下Android中类加载的流程 /libcore/ojluni/src/main/java/java/lang/ClassLoader.ja
  • Android实现读写USB串口数据的代码

    Android实现读写USB串口数据的代码
    最近在研究USB方面的内容;先后做了关于Android读写HID、串口设备的DEMO。本文比较简单,主要介绍的是Android实现读取串口数据的功能 废话不
  • Epoxy - 在RecyclerView中构建复杂界面
    Diffing 对于复杂数据结构支持的多个视图类型展示在屏幕上, Epoxy此时是尤其有用的. 在这些场景中, 数据可能会被网络请求, 异步 Observable, 用
  • Android性能优化的详细介绍

    Android性能优化的详细介绍
    性能优化是一个app很重要的一部分,一个性能优良的app从被下载到启动到使用都能给用户到来很好的体验。自然我们做性能优化也是从被下
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计