- 基于Kotlin的Spring Boot微服务实战
- 袁康
- 3940字
- 2020-11-24 13:01:00
3.4 集合
本节介绍Kotlin中的List、Set、Map集合及集合的操作,包括添加元素、查找元素、过滤元素、转换等。
3.4.1 集合概述
Kotlin标准库提供了一整套用于管理集合的工具,集合通常包含相同类型的一些(数目也可以为零)对象。集合中的对象称为元素或条目。以下是Kotlin相关的集合类型。
List是一个有序集合,可通过索引(反映元素位置的整数)访问元素。元素可以在List中出现多次。List有一组字,这些字的顺序很重要并且字可以重复。
Set是唯一元素的集合。它反映了集合的数学抽象:一组不重复的对象。一般来说,Set中元素的顺序并不重要。
Map(或者字典)是一组键值对。键是唯一的,每个键都刚好映射到一个值,值可以重复。Map对存储对象之间的逻辑连接非常有用。
只读集合类型是型变的。这意味着,如果类Rectangle继承自Shape,则可以在需要List <Shape>的任何地方使用List<Rectangle>。换句话说,集合类型与元素类型具有相同的类继承关系。Map在值(value)类型上是型变的,但在键(key)类型上不是。反之,可变集合不是型变的;否则将导致运行时故障。
如图3.1所示的是Kotlin集合和接口的继承关系。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-57-1.jpg?sign=1739190136-FWhX4O8iRDCIH5LLXzySDgNEMclDDrLA-0-84ea73392efd0c65cc74758f3a1e74e2)
图3.1 Kotlin集合和接口的继承关系
Collection<T>是集合层次结构的根。此接口表示一个只读集合的共同行为:检索大小、检测是否为成员等。Collection继承自Iterable<T>接口,它定义了迭代元素的操作。可以使用Collection作为适用于不同集合类型的函数的参数。MutableCollection是一个具有写操作的Collection接口,例如add及remove。
List<T>以指定的顺序存储元素,并提供使用索引访问元素的方法。索引从0开始,直到最后一个元素的索引,即(list.size-1)。List中的元素(包括空值)可以重复:List可以包含任意数量的相同对象或单个对象。如果两个List在相同的位置具有相同大小和相同结构的元素,则认为它们是相等的。MutableList是可以进行写操作的List,例如,用于在特定位置添加或删除元素。在某些方面,List与数组(Array)非常相似。但是,它们有一个重要的区别—数组的大小是在初始化时定义的,永远不会改变;而List未预定义大小。作为写操作的结果,可以更改List的大小—添加、更新或删除元素。
Set<T>存储唯一的元素;元素的顺序通常未定义。null元素也是唯一的,一个Set中只能包含一个null。当两个Set具有相同的大小并且一个Set中的每个元素都能在另一个Set中存在相同元素,则两个Set相等。MutableSet是一个带有来自MutableCollection的写操作接口的Set。Set的默认实现是LinkedHashSet,保持元素插入时的顺序。另一种实现方式是HashSet,不声明元素的顺序。
Map<K, V>不是Collection接口的继承者,但它也是Kotlin的一种集合类型。Map存储键值对(或条目);键是唯一的,但是不同的键可以与相同的值配对。Map接口提供特定的函数进行通过键访问值、搜索键和值等操作。无论键值对的顺序如何,包含相同键值对的两个Map是相等的。MutableMap是一个具有写操作的Map接口,可以使用该接口添加一个新的键值对或更新给定键的值。Map的默认实现是LinkedHashMap,迭代Map时保持元素插入时的顺序。反之,另一种实现方式是HashMap,不声明元素的顺序。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-58-1.jpg?sign=1739190136-NQfv8D3ezDrlSlFPfKBMmnis32q8Kj1q-0-df2d0eaec59e29336fbb2ddd5c606ff8)
创建集合的最常用方法是使用标准库函数listOf<T>()、setOf<T>()、mutableListOf<T>()、mutableSetOf<T>()。如果以逗号分隔的集合元素列表作为参数,编译器会自动检测元素类型。创建空集合时,必须明确指定集合类型。
同样地,Map也有这样的函数—mapOf()与mutableMapOf()。映射的键和值作为Pair对象传递(通常使用中缀函数to创建)。可以创建可写Map并使用写入操作填充它。apply()函数在初始化时使用。
创建没有任何元素的集合的函数有emptyList()、emptySet()与emptyMap()。创建空集合时,应指定集合将包含的元素类型。对于List,有一个接收List的大小与初始化函数的构造函数,该初始化函数根据索引定义元素的值。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-59-1.jpg?sign=1739190136-3V2ulgsbSJpknAz8bGv9KgmCpWlsDAWV-0-5ba80581921b3c9ec264f22aa5869102)
要创建与现有集合具有相同元素的集合,可以使用复制操作。标准库中的集合复制操作创建了具有相同元素引用的浅复制集合。因此,对集合元素所做的更改会反映在其所有副本中。但是toList()、toMutableList()、toSet()等创建了一个具有相同元素的新集合,如果在源集合中添加或删除元素,则不会影响副本。副本也可以独立于源集合进行更改。这些函数还可用于将集合转换为其他类型,例如,根据List构建Set,反之亦然。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-60-1.jpg?sign=1739190136-yhjwe2bRZvzigWm5X4diqOHDDhMiwman-0-aa1a8744bf5d288ed8497574cb8e3695)
Kotlin支持使用迭代器遍历集合中的元素,迭代器可以在不暴露集合内部结构的情况下,顺序遍历集合中的元素。迭代器适用于一个一个处理集合元素的场景。Set和List的iterator()函数提供了迭代器,迭代器初始指向集合的第一个元素,next()函数返回当前元素并指向下一个元素。迭代器可以遍历集合中的元素,但不能获取元素。如果需要再次遍历集合,需要重新创建一个迭代器。此外,用for或者forEach语句也可以遍历集合中的元素。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-60-2.jpg?sign=1739190136-jJyE6t2AbFOqqxTQVonCamOVZleECEBZ-0-a0f390942bb35e0aad2f4456ae357a47)
List有一个特殊的迭代器ListIterator,它支持双向遍历,后续遍历用hasPrevious()、previous(),可以用nextIndex()和previousIndex()获取元素下标。
可变集合提供MutableIterator,当遍历集合时,可以用remove移除元素。MutableListIterator可以修改、添加元素。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-60-3.jpg?sign=1739190136-vdjsZQ0nnizvZSYDlFIGpVdvcVjlJKhm-0-74520d1be2e5353b1c5d732f13810dbb)
Kotlin可以使用rangeTo创建一连串数值,通常用..替代rangeTo。这些数值可以用for语句遍历。如果要进行后续遍历,可以用downTo。这些数值的间隔通常是1,也可以用step指定步长。如果不需要遍历最后一个元素,可以用until。可以用in、!in判断某个元素是否在区间内。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-61-1.jpg?sign=1739190136-Bzooqs7W7Ke89ChUixcAAsWleg4HBZ7f-0-ccb3e4402ab0c062ae1ab59092e6570a)
Kotlin提供序列类型Sequence<T>。当迭代过程包含多个步骤时,每一步执行完,会产生一个结果,下面的步骤会在这个结果的基础上执行。序列中的每一个元素都要依次执行操作步骤,迭代器对集合中的所有元素执行完一个操作过程后再执行下一个。序列可避免产生中间结果,可以提高整个处理链的性能。
通常可以用sequenceOf()构建序列;可以用asSequence()将List、Set转换为序列;可以用generateSequence()显示指定序列的第一个元素,函数返回null时序列停止增长;序列也可以用组块生成,函数包含Lambda表达式,包含yield()、yieldAll()函数。yield()的参数是一个元素,yieldAll()的参数可以是一个集合,也可以是一个序列,这个序列可以无限长。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-62-1.jpg?sign=1739190136-71amuevFQAoiOvwhsG473esCFPTLyzrk-0-2719b21e7abdcb90d275b567f08b3eba)
序列支持无状态的操作,如filter、take、map、drop等。下面的例子展示了序列的处理过程,对每个元素都会执行filter、map、take方法,在找出4个元素后,不再遍历其余元素。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-62-2.jpg?sign=1739190136-kjCevMTJFcP2DITeerTqtBMrVooVWDcM-0-de240e90f7e8507a517b47684d8681e6)
3.4.2 集合操作
Kotlin标准库提供了很多函数操作集合,如set、add、search、sorting、filtering、transformations等。
集合有如下操作:转换(transformations)、过滤(filtering)、加减(plus and minus operators)、分组(grouping)、获取部分集合(retrieving collection parts)、排序(ordering)、聚合操作(aggregate operations)。
集合操作不会改变集合本身,而是生成一个新的集合存放处理后的结果。此外,还可以指定一个可变对象存放处理后的结果。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-63-1.jpg?sign=1739190136-00Bnan5kFQiJstuORzfhR0WOFUjjnZc6-0-eecdc1ed873745a7f0da7924d3dd8785)
Kotlin提供了很多集合转换的扩展操作,这些操作会产生新的集合。下面介绍几种转换操作:映射(mapping),用Lambda表达式对当前集合中的元素进行处理,生成一个新的集合,两个集合中元素的顺序一致。当产生null元素时,可以用mapNotNull()函数将其过滤掉。当转换映射时,可以用mapKeys处理key值,或者用mapValues处理value值。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-63-2.jpg?sign=1739190136-rnbtM82H1xMoGhRTHJUCrfqnaiYX36NV-0-24f0d73496dca248be6258f0418bbb0c)
双路合并(zip)操作,将两个集合中的元素合并为一个List,List中每个元素成对出现,是Pair类型的。如果两个集合大小不一样,zip操作取较小的集合长度。此外,在zip操作的基础上,还可以进行Lambda操作,最终返回的List元素集合就是由Lambda操作后的元素组成的。相反,使用unzip函数可以进行反向操作。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-64-1.jpg?sign=1739190136-zhUAehuuu9p3d2fk5ngoPkkvGU3GykVb-0-4f641c2d347ca51c252e53bd81bc9d9f)
关联(association)操作,可以对集合中的元素进行处理,以生成一个映射。基本的关联函数是associateWith(),集合中的元素作为映射的key,转换后的元素作为映射的value。associateBy()函数将集合中的元素作为映射的value,转换后的元素作为映射的key。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-64-2.jpg?sign=1739190136-38D1RLEMPM4NdwC114aAsobVmUi8pgfn-0-5203a4ea503824f12b0c8d8b8cd203b3)
打平(flattern)操作,flattern()函数将若干集合中的元素放在一个集合中;flatternMap()函数对若干集合进行映射操作,然后再打平。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-65-1.jpg?sign=1739190136-Pqc4gRHJJjqluEbXJDJ0Tp5KfRi8nfam-0-d169ba9b49aae85dd1e252b3477bbe9f)
字符串处理操作,可以用joinToString()函数将集合中的元素生成一个字符串,用joinTo()函数将生成的字符串拼接在另一个字符串后。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-65-2.jpg?sign=1739190136-Q8WaMjZREYw8Vymd0ao6IGp4nA95hOY1-0-f504fcb773a1ec486e659c4573d08dda)
过滤操作在集合处理中使用广泛。Kotlin使用谓词函数实现过滤操作,使用Lambda表达式处理集合的元素,并返回一个布尔值。如果需要使用集合元素的下标,可以使用filterIndexed()函数。如果需要使用相反条件过滤,可以使用filterNot()函数。如果需要过滤类型,可以使用filterIsInstance<T>()函数。需要过滤空,可以使用filterNotNull()函数。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-65-3.jpg?sign=1739190136-JvwB9mDcHDWieOLIKrvF4pecFVbyDoWJ-0-771334967c69e66fa17fb579a574cff4)
可以使用partition()函数对集合进行划分,一部分满足过滤条件,另一部分不满足过滤条件。可以使用any()(至少有一个)、none()(一个也没有)、all()(全部满足)来检验谓词函数。
集合还可以进行加减操作,运算符的左值是集合,右值可以是一个元素或者集合。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-66-1.jpg?sign=1739190136-CW8QoqvnI9771U2z1H0bqMncpTzY46SN-0-c895efdac9aecfd86c3433e65144add5)
Kotlin还提供了分组操作。groupBy函数使用一个Lambda表达式,返回一个映射。映射的key是Lambda表达式返回的值,value是集合中的元素。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-66-2.jpg?sign=1739190136-mifFSS5XUfoXDizdZpRjDu6pcTlWXK2N-0-04dac1dcf9840d1b181b83d05ecc27d3)
Kotlin提供了丰富的操作以获取集合的一部分。slice()函数根据给定下标返回集合中的元素。take()函数从第一个元素开始,截取给定个数的集合元素;如果截取个数大于集合长度,返回整个集合元素。drop()函数从第一个元素开始,丢弃给定个数的元素,然后返回其余元素。如果要使用谓词函数,可以用takeWhile()、takeLastWhile()、dropWhile()函数、dropLastWhile()。chunked()函数将集合分块,遍历集合元素,达到给定数量,生成一个List,直到最后一个元素。windowed()函数从第一个元素开始进行遍历,可以指定窗口大小。此外,windowed()函数可以指定步长step等参数。如果滑动窗口中只有两个元素,可以用zipWithNext()函数。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-67-1.jpg?sign=1739190136-Nr4i5w7Lul66Fp8m1atuzD0IkvPC2P54-0-f5f25f347ffe6456be6a156a0db6d260)
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-68-1.jpg?sign=1739190136-sgqMErH5oeutDCtzteKQANxE5e4KyGV9-0-7a2e49c7036df85c25a747a4f8058eba)
如果要获取集合中的单个元素,可以使用elementAt()按位置获取,因first()获取第一个元素,用last()获取最后一个元素,也可以在first()或者last()后用谓词函数按条件获取。用random()函数可随机获取元素,用contains()检测是否存在某个元素,用containsAll()检测是否存在多个元素,用isEmpty()、isNotEmpty()判断集合是否为空。
Kotlin可以用Comparable接口对自定义的类型进行排序,Kotlin自带的类型默认支持排序,数值型按数值大小排序,字符型按照字母顺序排序。此外,还可以用Comparator自定义顺序,compareBy()是Comparator的简单写法。自然顺序可以用sorted()和sortedDescending()进行升序或降序操作。自定义顺序可以用sortedBy()和sortedByDescending()进行升序或降序操作。倒序可以用reversed()、asReversed()。随机顺序可以用shuffled()。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-68-2.jpg?sign=1739190136-Q4Nksu32LZwtqjdIvwPf7OD2RG7LFZOe-0-c11b38656203732e524a2edd26b98c01)
集合的聚合操作函数有求最小值min()、最大值max()、平均值average()、求和sum()、计数count(),带有函数的求最大值/最小值为maxBy()/minBy(),Comparator对象的求最大值/最小值为maxWith()/minWith(),带有谓词函数的求和为sumBy(),返回Double类型的求和为sumDouble(),累加为fold()、reduce(),fold()有初始值,reduce()开始时用前两个元素作为参数。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-69-1.jpg?sign=1739190136-nRfoZuHod86EDAHpYWwsumMXmzLuQjJ1-0-7cf249ea0267db657cb86afb5ed824b6)
集合可以通过add()添加单个元素,addAll()添加多个元素,remove()删除元素,retainAll()保留符合条件的元素,clear()清空集合,minusAsign()和minus()删除元素。
3.4.3 List、Set、Map相关操作
List是使用广泛的集合,List可以按索引取元素,getOrElse()函数若取不到元素就返回默认值,getOrNull()函数若取不到元素就返回null。对于有序List,可以用binarySearch()进行二分查找,此外,可以自定义排序规则。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-70-1.jpg?sign=1739190136-Fb3HYslOGim9kkoklf3Q7YXaxlsrDtLs-0-59ac4926d255a9c0647c7f7903c52638)
Set提供了求交集intersect()、合并union()、剔除交集元素subtract()等操作。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-71-1.jpg?sign=1739190136-3lNTWazFyXxh8GPPKT0gaT5Dxeo2LfVa-0-35f0ee4fda3121139f4ddb74402ed3fe)
Map提供了取键、值操作,过滤键、值操作,plus和minus操作,以及添加和更新操作。
![](https://epubservercos.yuewen.com/CD9CCF/18519309308427806/epubprivate/OEBPS/Images/39715-00-71-2.jpg?sign=1739190136-36GREP2k1eYocaP03PJoTbThdVRTLiES-0-9d8e5fe97b00919794a79643e338fc41)