Kotlin中的集合框架

1,646 阅读9分钟

博客地址:sguotao.top/Kotlin-2018…

欲善其事,必利其器,在Java中集合框架占有很重要的地位,在Kotlin中也同样,想要能够在使用这些集合的时候,能够根据现实中的业务场景,信手拈来,就需要对这些集合有比较全面的了解。本篇将对比Java中的集和框架,对Kotlin中的集合框架进行梳理,首先简单回顾一下Java中的集合框架,接着重点介绍Kotlin中的集合框架,最后介绍Kotlin提供的一些关于集合框架的高阶函数。

Java中的集合框架

集合框架的用途是提供一个容器,用来存储对象,具体使用哪个容器,需要根据业务场景的需求,选择合适的容器。如果从容器的用途这个角度,Java提供了两种容器,Collection和Map,Collection是用来存储单个元素形式对象的集合,Map是用来存储键/值映射这样成对元素的集合。了解了这个后,再来看一下Java集合框架的全家福。

20181031154096967157812.png

从图中可以看到我们常用的集合如LinkedLlist、ArrayList、HashSet、HashMap等清晰的继承关系,Java将一些对集合常用的方法封装在Collections和Arrays两个类中。集合都可以产生一个迭代器,List还能产生一个ListIterator的迭代器。最后简单对Java的集合框架总结一下:

  1. Java提供了两种类型的容器,Collection和Map;
  2. 常用的集合List和Set都是Collection接口的实现类,List存储的对象之间有指定的顺序,存储的对象可以重复,Set存储的对象之间没有指定的顺序,存储的对象不可以重复。

Kotlin中的集合框架

Kotlin中的集合框架,去掉了Java中历史遗留的一些结构,同时集合类型在Kotlin中可以是只读的,也可以是可变的,于是Kotlin中的集合框架就与Java中集合框架有如下的映射关系:

Java中的类型 Kotlin中的只读类型 Kotlin中的可变类型
Iterator<T> Iterator<T> MutableIterator<T>
Iterable<T> Iterable<T> MutableIterable<T>
Collection<T> Collection<T> MutableCollection<T>
Set<T> Set<T> MutableSet<T>
List<T> List<T> MutableList<T>
ListIterator<T> ListIterator<T> MutableListIterator<T>
Map<K, V> Map<K, V> MutableMap<K, V>
Map.Entry<K, V> Map.Entry<K, V> MutableMap.MutableEntry<K,V>

相应的给出Kotlin的集合框架图,从图中也可以看出,Kotlin集合中的可变类型都实现了只读类型,同时将一些对集合操作的方法,封装在了_Collections和_Arrays等文件中。由于Kotlin中存在扩展函数,因此在使用这些方法时,不是直接通过文件名+点的方式进行调用,而是就像集合自身的方法一样进行调用。从Java的角度看,就像这些方法是这些集合的成员函数。

20181031154096969715136.png

了解了Kotlin的集合框架,那么实际用到的集合有哪些呢?在Kotlin collections包中可以找到这样一个文件TypeAliasesKt.class文件。

@file:kotlin.jvm.JvmVersion
package kotlin.collections

@SinceKotlin("1.1") public typealias RandomAccess = java.util.RandomAccess

@SinceKotlin("1.1") public typealias ArrayList<E> = java.util.ArrayList<E>
@SinceKotlin("1.1") public typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
@SinceKotlin("1.1") public typealias HashMap<K, V> = java.util.HashMap<K, V>
@SinceKotlin("1.1") public typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
@SinceKotlin("1.1") public typealias HashSet<E> = java.util.HashSet<E>

// also @SinceKotlin("1.1")
internal typealias SortedSet<E> = java.util.SortedSet<E>
internal typealias TreeSet<E> = java.util.TreeSet<E>

可以看到,我们常用的一些集合如ArrayList、LinkedHashMap、HashMap、LinkedHashSet、HashSet等,在Kotlin中实际上对应的就是Java中的这些集合,Kotlin只是换了个别名而已,这些集合的存储结构与Java中是一样的,所以可以如同在Java中一样的方式使用这些集合。

集合中的高阶函数

前面说到Kotlin将一些对集合的扩展函数都封装在了_Collections、_Arrays这样的文件中,那么究竟有哪些扩展函数呢?了解一下,可以省去写很多工具方法的时间,而且稳定性可能会优于我们自己的工具方法。由于扩展函数数量比较多,这里将按照函数实现的功能,将这些函数分成下面几类。

一 总数函数

1.any 如果有一个元素符合给出的判断条件,返回true*

val list = listOf(1, 2, 3, 4, 5, 6)
val listwithNull = listOf(1, 2, 3, 4, null, 5, null, 6, null)
assertTrue(list.any { it % 2 == 0 })

2.all 如果全部的元素符合给出的判断条件,返回true

assertTrue { list.all { it > 0 } }

3.count 返回符合给出判断条件的元素的个数

assertEquals(3, list.count { it % 2 == 0 })

4.fold 在一个初始值的基础上从第一项到最后一项进行累加

assertEquals(220, list.fold(10) { total, next -> total + next * 10 })

5.foldRight 与fold类似,但顺序是从最后一项到第一项

assertEquals(31, list.foldRight(10) { total, next -> total + next })

6.forEach 遍历所有元素并执行给定操作

list.forEach() { print(it) }

7.forEachIndexed 与forEach类似,同时可以获取元素的index

list.forEachIndexed() { index, value -> println("position:$index,value:$value") }

8.max 返回最大一项,如果没有返回null

assertEquals(6, list.max())

9.maxBy 根据给定的函数,返回最大一项,如果没有返回null

assertEquals(1, list.maxBy { -it })

10.min 返回最小一项,如果没有返回null

assertEquals(1, list.min())

11.minBy 根据给定的函数,返回最小的一项,如果没有返回null

assertEquals(6, list.minBy { -it })

12.none 如果没有任何元素与给定的函数匹配,返回true

assertTrue { list.none() { it >= 7 } }

13.reduce 与fold类似,但没有初始值

assertEquals(21, list.reduce() { total, next -> total + next })

14.reduceRight 与reduce类似,但顺序是从最后一项到第一项

assertEquals(21, list.reduceRight() { total, next -> total + next })

15.sumBy 返回每一项通过函数转换后的数据总和

assertEquals(-21, list.sumBy { -it })

二 过滤函数

16.drop 返回包含去掉前n个元素的所有元素的列表

assertEquals(listOf(5, 6), list.drop(4))

17.dropWhile 返回去掉满足指定函数要求的,从第一个元素开始的所有元素的列表

assertEquals(listOf(4, 5, 6), list.dropWhile { it < 4 })

18.dropLastWhile 返回去掉指定函数要求的,从最后一个元素开始的所有元素的列表

assertEquals(listOf(1, 2, 3, 4), list.dropLastWhile { it > 4 })

19.filter 保留所有满足指定函数要求的元素

assertEquals(listOf(2, 4, 6), list.filter { it % 2 == 0 })

20.filterNot 过滤掉所有满足指定函数要求的元素

assertEquals(listOf(1, 3, 5), list.filterNot { it % 2 == 0 })

21.filterNotNull 保留所有不为null的元素

assertEquals(listOf(1, 2, 3, 4, 5, 6), listwithNull.filterNotNull())

22.slice 保留list中指定index的元素

assertEquals(listOf(2, 4, 5), list.slice(listOf(1, 3, 4)))

23.take 保留从第一个元素开始的n个元素

assertEquals(listOf(1, 2, 3), list.take(3))

24.takeLast 保留从最后一个元素开始的n个元素

assertEquals(listOf(4, 5, 6), list.takeLast(3))

25.takeWhile 保留从第一个元素开始,满足指定函数的元素

assertEquals(listOf(1, 2), list.takeWhile { it < 3 })

三 映射函数

26.flatMap 遍历每一个元素,为每一个元素创建一个集合,最后把所有集合合并为一个集合

print(list)//list:[1,2,3,4,5,6]
//[1,2],[2,3],[3,4],[4,5],[5,6],[6,7] => [1,2,2,3,3,4,4,5,5,6,6,7]
println(list.flatMap { listOf(it, it + 1) })

27.groupBy 返回一个根据指定函数分组的map

println(mapOf("odd" to listOf(1, 3, 5)))
println(mapOf("even" to listOf(2, 4, 6)))
println(list.groupBy { if (it % 2 == 0) "even" else "odd" })

28.map 返回一个,每个元素都按照指定函数进行转换后的集合

assertEquals(listOf(2, 4, 6, 8, 10, 12), list.map { it -> it * 2 })

29.mapIndexed 返回一个,每个元素按照包含元素index的指定函数转换后的集合

assertEquals(listOf(0, 2, 6, 12, 20, 30), list.mapIndexed { it, index -> index * it })

30.mapNotNull 返回一个过滤掉null元素,并且非null元素按照指定函数进行转换后的集合

println(listwithNull)
println(listwithNull.mapNotNull { it })

四 元素操作函数

31.contains 如果指定元素在集合中,返回true

assertTrue(list.contains(1))

32.elementAt 返回指定index对应的元素,如果index越界,抛IndexOutOfBoundsException

assertEquals(1, list.elementAt(0))

33.elementAtOrElse 返回指定index对应的元素,如果index越界,返回指定函数中设置的默认值

assertEquals(20, list.elementAtOrElse(10, { 2 * it }))

34.elementAtOrNull 返回指定index对应的元素,如果index越界,返回null

assertNull(list.elementAtOrNull(10))

35.first 返回第一个满足指定函数的元素,如果没有,抛出NoSuchElementException

assertEquals(2, list.first() { it % 2 == 0 })

36.firstOrNull 返回第一个满足指定函数的元素,如果没有返回null

assertNull(list.firstOrNull() { it < 0 })

37.indexOf 返回指定元素的第一个index,如果不存在返回-1

assertEquals(-1, list.indexOf(7))

38.indexOfFirst 返回第一个满足指定函数的元素的index,如果不存在返回-1

assertEquals(1, list.indexOfFirst { it % 2 == 0 })

39.indexOfLast 返回最后一个满足指定函数的元素的index,如果不存在返回-1

assertEquals(5, list.indexOfLast { it % 2 == 0 })

40.last 返回最后一个满足指定函数的元素,如果没有,抛出NoSuchElementException

println(list.last() { it % 2 == 0 })

41.lastIndexOf 返回指定元素的最后一个index,如果不存在返回-1

println(listwithNull.lastIndexOf(null))

42.lastOrNull 返回最后一个满足指定函数的元素,如果没有返回null

assertNull(list.lastOrNull() { it < 0 })

43.single 返回满足指定函数的单个元素,如果没有,或者满足条件的元素个数超过一个,抛出异常

assertEquals(1, list.single() { it < 2 })

44.singleOrNull 返回满足指定函数的单个元素,如果没有,或者满足条件的元素个数超过一个,返回null

println(list.singleOrNull() { it % 2 == 0 })

五 生产函数

45.partition 将一个给定的集合分割成两个集合,第一个集合是由匹配指定函数,返回true的元素组成。第二个集合是由匹配指定函数,返回false的元素组成。

assertEquals(Pair(listOf(1, 3, 5), listOf(2, 4, 6)), list.partition { it % 2 != 0 })

46.plus 返回一个包含原集合和给定集合中所有元素的集合,可以使用“+”操作符

assertEquals(listOf(1, 2, 3, 4, 5, 6, 6, 7, 8), list + listOf(6, 7, 8))

47.plusElement 在集合中添加元素

println(list.plusElement(7))

48.zip 将两个集合按照下标进行配对,组成的Pair是由两个集合中相同index的元素组成,如果两个集合长度不一致,取短的集合的长度。

assertEquals(listOf(Pair(1, 'x'), Pair(2, 'y')), list.zip(listOf('x', 'y')))

49.unzip 作用在包含Pair的集合上,依次取各个Pair的first和second值,放入List和List中,然后返回包含List和List的Pair

val listPair = listOf(Pair(1, 2), Pair('a', 'b'), Pair(3, 4), Pair('c', 'd'))
println(listPair)
println(listPair.unzip())

六 顺序函数

50.reversed 返回一个与指定list顺序相反的list

println(list)
println(list.reversed())

51.sorted 升序排序

println(list.sorted())

52.sortBy 返回一个按照指定函数变换后的升序排序

println(list.sortedBy { it -> it % 3 })

53.sortDescending 降序排序

println(list.sortedDescending())

54.sortDescendingBy 返回一个按照指定函数变换后的降序排序

println(list.sortedByDescending { it -> it % 3 })

学习资料

  1. Kotlin Bootcamp for Programmers
  2. Kotlin Koans