Kotlin集合操作符全覆盖

2,889 阅读7分钟

1、转换

1.1 映射

  • 映射转换指将原始集合元素调用转换函数后的结果创建一个新集合。

  • 基本的映射函数是 map() 。它将给定的lambda函数用于原始集合的每个元素,并返回lambda结果列表。转换后的元素顺序与原始列表的顺序一致。

  • 如需用到元素索引,应使用 mapIndexed() 函数。

  • mapNotNull() 可过滤掉在映射转换中产生的null值。

  • Map类在做映射转换时,有两个选择:转换键使值不变;反之亦然。要将转换应用与键,使用 mapKeys() ;反过来, mapValues() 用来转换值。这两个转换函数都使用Map条目作为参数,所以可以同时操作键与值,区别在于转换函数的结果是应用在键上还是值上。

  • 映射转换使用于将一个集合转换为另一个集合

1.2 双路合并

  • 双路合并转换是将两个列表中具有相同位置的元素根据转换函数构建配对。

  • 基本函数为 zip() ,它有两种调用方式:

    1. 在一个列表上以另一个列表作为参数调用,返回以Pair对象为泛型的List。此种方式也可以中缀形式调用 a zip b 。

    2. 使用带有两个参数的转换函数来调用 zip() :参数列表和转换函数。结果为两个列表相同位置元素调用转换函数的返回值。

  • 如果两个列表大小不同,则 zip() 的结果大小为较小列表的大小,结果中不包含较大列表的后续元素。

  • 双路合并适用于合并两个List。

1.3 关联

  • 关联转换指将列表元素和其与转换函数的结果来构建Map。
  • 列表元素作为键构建Map时使用 associateWith() 函数;作为值构建Map时使用 associateBy()
    associateWith()函数源码
    associateBy()函数源码
  • 当使用associate_()函数时,应注意转换后的键值不可重复,否则会有数据丢失问题。
  • 关联转换适用于将List转换为Map。
    关联转换示例

1.4 打平

  • 打平转换指对嵌套列表进行打平访问,有 flatten() 函数和 flatMap() 函数可使用。
  • flatten() 函数可在一个嵌套列表调用,将嵌套列表中的所有元素打平为一个列表返回。
    flatten()函数源码,应注意它的调用者必须为嵌套的可迭代对象
  • flatMap() 函数作为Kotlin集合操作符有两种理解,第一种可以理解为一种更灵活的打平转换,它将原始集合中的元素迭代调用返回值为集合的lambda表达式,为原始集合的每个元素创建一个新集合然后将它们打平放入到一个集合中返回;第二种可以将它理解为合并多个集合:以原始集合中的每个元素作为参数调用返回值为集合的lambda表达式,然后将每个lambda的结果合并为一个集合。
    flatMap()函数源码
  • 打平转换适用于对多维集合进行降维操作。
    打平转换示例

2、过滤

  • 过滤的基本函数为 filter() ,需要注意的是过滤函数不会改变原始集合,因此它既可以用于可变集合也可以用于不可变集合。

  • filter() 函数接收一个返回值为布尔类型的lambda表达式(我们称这样的表达式为谓词),并将原始集合中匹配lambda的元素作为集合返回。

    • 对于List和Map,过滤结果是List;对于Map,过滤结果还是Map。
      filter()函数源码
    • 如果需要使用否定条件过滤集合,则可以使用 filterNot() 函数,它将不匹配lambda表达式的元素作为集合返回。
    • 如果在过滤中需要使用元素在集合中的位置,则可以使用 filterIndexed() 函数。
    • filterIsInstance() 函数会筛选出给定类型的集合元素,在一个List< Any >上调用时,filterIsInstance< T >()返回一个List< T >。
    • filterNotNull() 返回所有的非空元素,从而能够将所有元素视为非空对象
  • 划分过滤函数 partition() ,将匹配lambda表达式的元素作为第一个List,不匹配的作为第二个List,返回一个以List为泛型的Pair对象。

    partition()函数源码

  • 检测谓词

    • 如果至少有一个元素匹配给定的谓词,那么 any() 返回true。
    • 如果没有任何元素匹配给定的谓词,那么那么 none() 返回true。
    • 如果所有元素都匹配给定的谓词,那么那么 all() 返回true。注意,在一个空集合上使用任何有效的谓词去调用那么 all() 都会返回true。

3、分组

  • 分组的基本函数是 groupBy() ,接收一个或两个lambda表达式并返回一个Map。
    • 当只传入一个lambda时,lambda表达式的结果作为键,产生此结果的原始集合的元素作为值。
      groupBy()函数源码
    • 当传入两个lambda时,第二个lambda可自定义结果Map中的值,由第一个lambda表达式生成的键映射到第二个lambda表达式的结果。
      groupBy()函数源码
      groupBy()函数示例

4、读取

  • slice() 返回具有指定索引的集合元素列表,既可以传入区间也可以传入整数值的集合。
    slice()函数源码
  • 要从头或从尾获取指定数量的元素,使用 take() takeLast() 函数。当传入数字大于集合的大小时,两个函数返回整个集合。
  • 要从头或从尾去除指定数量的元素,使用 drop() dropLast() 函数。
  • chunked() 函数可将集合分解为指定大小的“块”,也可以传入lambda来对分块后的集合做转换。
  • windowed() 函数是可以定义步长的 chunked() chunked() 函数在分块时,步长就是传入的size,而 windowed() 函数默认步长为1,结果包含了从所有元素开始的块。从源码也可以看到 chunked() 函数在实现中就是调用的 windowed()
    chunked()和windowed()函数源码

5、排序

  • 基本函数为 sorted() sortedDescending() ,它们按自然顺序升序或降序返回集合的元素。适用于Comparable元素的集合。
    sorted()函数源码
  • sortedBy() sortedByDescending() 函数接收一个将集合元素映射为Comparable值的转换函数,并以该值的自然顺序对集合排序。
  • sortedWith() 函数可为集合排序定义自定义顺序,可以提供自己的Comparator。
    sortedBy()和sortedWith()函数源码,可以看到,sortedBy在内部也是调用的sortedWith()
  • 倒序 reversed() asReversed() 函数区别在于 reversed() 函数会返回一个新集合,后续对原始集合的改变不会影响到先前得到的倒序列表; asReversed() 函数获取的是原始集合的一个反向视图,它会比 reversed() 函数更轻量,但后续对原始集合的改变都会放映在其反向视图中。

6、聚合

  • min() max() 分别返回最小和最大的元素; maxBy() minBy() 接受一个选择器函数并返回使选择器返回最大或最小值的元素; maxWith() minWith() 接受一个Comparator对象并且根据此Comparator对象返回最大或最小元素。
  • sum() 返回数字集合中元素的总和; sumBy() sumByDouble() 接受一个函数并返回对所有元素调用此函数的返回值的总和。
  • reduce() fold() 函数适用于更特定的求和操作,它们一次将所提供的操作应用于集合元素并返回累积的结果。操作有两个参数:先前的累积值和集合元素。这两个函数的区别在于: flod() 接收一个初始值并将其作为第一步的累积值,而 reduce() 的第一步是将集合第一个和第二个元素作为第一步的操作参数。
    reduce()和fold()函数源码
    reduce()和fold()示例

7、最后

kotlin的集合操作符讲完了,其实每个操作符的源码都很简单,很多文字上理解不好的看看源码很容易就懂了,配合思维导图掌握好并不难,难在如何在实际项目中配合业务使用。PS:如果你的项目有一堆不靠谱的接口,那么机会就来了。