Kotlin没有本身的集合库,彻底依赖Java标准库中的集合类,并经过扩展函数增长特性来加强集合。意味着Kotlin与Java交互时,永远不须要包装或者转换这些集合对象,大大加强与Java的互操做性。html
Kotlin与Java最大的不一样之一就是:Kotlin将集合分为只读集合和可变集合。这种区别源自最基础的集合接口:kotlin.collections.Collection
。该接口能够对集合进行一些基本操做,但无任何添加和移除元素的方法。java
只有实现 kotlin.collections.MutableCollection
接口才能够修改集合的数据。MutableCollection
接口继承自 Collection
,并提供添加、移除和清空集合元素的方法。当一个函数接收 Collection
,而不是 MutableCollection
,即意味着函数不对集合作修改操做。android
Iterable<T>
定义了迭代元素的操做, Collection 继承自
Iterable<T>
接口,从而具备对集合迭代的能力。
Kotlin中建立集合通常都是经过 Collection.kt 中的顶层函数进行建立。具体方法以下:数组
集合类型 | 只读 | 可变 |
---|---|---|
List | listOf | mutableList、arrayListOf |
Set | setOf | mutableSetOf、hashSetOf、linkedSetOf、sortedSetOf |
Map | mapOf | mutableMapOf、hashMapOf、linkeMapOf、sortedMapOf |
像 arrayListOf 这些指明集合类型的顶层函数,建立时都是对应着Java相应类型的集合。为了弄清楚 Kotlin 的生成的只读集合(listOf
、setOf
和 mapOf
)与可变集合(mutableList
、mutableSetOf
和 mutableMapOf
)生成的是什么Java类型集合,作了一个小实验(分别对应空集合、单元素集合和多元素集合):安全
#daqiJava.java
public static void collectionsType(Collection collection){
System.out.println(collection.getClass().getName());
}
public static void mapType(Map map){
System.out.println(map.getClass().getName());
}
复制代码
#daqiKotlin.kt
fun main(args: Array<String>) {
val emptyList = listOf<Int>()
val emptySet = setOf<Int>()
val emptyMap = mapOf<Int,Int>()
val initList = listOf(1)
val initSet = setOf(2)
val initMap = mapOf(1 to 1)
val list = listOf(1,2)
val set = setOf(1,2)
val map = mapOf(1 to 1,2 to 2)
println("空元素只读集合")
collectionsType(emptyList)
collectionsType(emptySet)
mapType(emptyMap)
println("单元素只读集合")
collectionsType(initList)
collectionsType(initSet)
mapType(initMap)
println("多元素只读集合")
collectionsType(list)
collectionsType(set)
mapType(map)
println("-----------------------------------------------------------------")
val emptyMutableList = mutableListOf<Int>()
val emptyMutableSet = mutableSetOf<Int>()
val emptyMutableMap = mutableMapOf<Int,Int>()
val initMutableList = mutableListOf(1)
val initMutableSet = mutableSetOf(2)
val initMutableMap = mutableMapOf(1 to 1)
val mutableList = mutableListOf(1,2)
val mutableSet = mutableSetOf(1,2)
val mutableMap = mutableMapOf(1 to 1,2 to 2)
println("空元素可变集合")
collectionsType(emptyMutableList)
collectionsType(emptyMutableSet)
mapType(emptyMutableMap)
println("单元素可变集合")
collectionsType(initMutableList)
collectionsType(initMutableSet)
mapType(initMutableMap)
println("多元素可变集合")
collectionsType(mutableList)
collectionsType(mutableSet)
mapType(mutableMap)
}
复制代码
结果:bash
listOf
、
setOf
和
mapOf
)与可变集合(
mutableList
、
mutableSetOf
和
mutableMapOf
)对应Java集合的关系表:
方法 | Java类型 |
---|---|
listOf() | kotlin.collections.EmptyList |
setOf() | kotlin.collections.EmptySet |
mapOf() | kotlin.collections.EmptyMap |
listOf(element: T) | java.util.Collections$SingletonList |
setOf(element: T) | java.util.Collections$SingletonSet |
mapOf(pair: Pair<K, V>) | java.util.Collections$SingletonMap |
listOf(vararg elements: T) | java.util.Arrays$ArrayList |
setOf(vararg elements: T) | java.util.LinkedHashSet |
mapOf(vararg pairs: Pair<K, V>) | java.util.LinkedHashMap |
mutableList() | java.util.ArrayList |
mutableSetOf() | java.util.LinkedHashSet |
mutableMapOf() | java.util.LinkedHashMap |
只读集合类型是型变的。当类 Rectangle
继承自 Shape
,则能够在须要 List<Shape>
的任何地方使用 List<Rectangle>
。 由于集合类型与元素类型具备相同的子类型关系。 Map
在值类型上是型变的,但在键类型上不是。数据结构
可变集合不是型变的。 MutableList <Rectangle>
是 MutableList <Shape>
的子类型,当你插入其余 Shape
的继承者(例如,Circle
),从而违反了它的 Rectangle
类型参数。多线程
对于任何类型,均可以对其声明为可空类型,集合也不例外。你能够将集合元素的类型设置为可空,也能够将集合自己设置为可空,须要清楚是集合的元素可空仍是集合自己可空。并发
学习 Kotlin 的时候,经常被告知 Kotlin 直接使用的是原生 Java 集合,抱着探究真相的心态,点进了建立集合的顶层方法 mutableListOf()
。dom
#Collections.kt
public fun <T> mutableListOf(vararg elements: T): MutableList<T> =
if (elements.size == 0)
ArrayList()
else
ArrayList(ArrayAsCollection(elements, isVarargs = true))
复制代码
在源码中看到了熟悉的ArrayList,那是Java的ArrayList
嘛?继续点进ArrayList
,发现是一个Kotlin定义的ArrayList
:
#ArrayList.kt
expect class ArrayList<E> : MutableList<E>, RandomAccess {
constructor()
constructor(initialCapacity: Int)
constructor(elements: Collection<E>)
//... 省略一些来自List、MutableCollection和MutableList的方法
//这些方法只有声明,没有具体实现。
}
复制代码
逛了一大圈,并无找到一丝 Java 的 ArrayList
的痕迹.... Excuse me??? 说好的使用 Java 的 ArrayList
,但本身又建立了一个ArrayList
.... 。最后将目标锁定在类声明的 expect 关键字,这是什么?最后在Kotlin官网中查到,这是Kotlin 平台相关声明的预期声明!
在其余语言中,一般在公共代码中构建一组接口,并在平台相关模块中实现这些接口来实现多平台。然而,当在其中某个平台上已有一个实现所需功能的库,而且但愿直接使用该库的API而无需额外包装器时,这种方法并不理想。
Kotlin 提供平台相关声明机制。 利用这种机制,公共模块中定义预期声明,而平台模块提供与预期声明相对应的实际声明。
要点:
官网提供一个简单的例子:
#kt
//在公共模块中定义一个预期声明(不带任何实现)
expect class Foo(bar: String) {
fun frob()
}
fun main() {
Foo("Hello").frob()
}
复制代码
相应的 JVM 模块提供实现声明和相应的实现:
#kt
//提供实际声明
actual class Foo actual constructor(val bar: String) {
actual fun frob() {
println("Frobbing the $bar")
}
}
复制代码
若是有一个但愿用在公共代码中的平台相关的库,同时为其余平台提供本身的实现。(像Java已提供好完整的集合库)那么能够将现有类的别名做为实际声明:
expect class AtomicRef<V>(value: V) {
fun get(): V
fun set(value: V)
fun getAndSet(value: V): V
fun compareAndSet(expect: V, update: V): Boolean
}
actual typealias AtomicRef<V> = java.util.concurrent.atomic.AtomicReference<V>
复制代码
而Java集合类做为实际声明的别名被定义在 TypeAliases.kt 中。这是我不知道 TypeAliases.kt 时的查找流程:
# TypeAliases.kt
@SinceKotlin("1.1") public actual typealias RandomAccess = java.util.RandomAccess
@SinceKotlin("1.1") public actual typealias ArrayList<E> = java.util.ArrayList<E>
@SinceKotlin("1.1") public actual typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
@SinceKotlin("1.1") public actual typealias HashMap<K, V> = java.util.HashMap<K, V>
@SinceKotlin("1.1") public actual typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
@SinceKotlin("1.1") public actual typealias HashSet<E> = java.util.HashSet<E>
复制代码
Kotlin定义一些集合类做为集合的通用层(使用 expect 定义预期声明),并将现有的Java集合类的别名做为实际声明,从而实如今JVM上直接使用Java的集合类。
能够从Kotlin官方文档中集合的变迁来观察(ArrayList为例):
从本来无ArrayList.kt,只有一系列对ArrayList.java的扩展属性与方法
-> 使用别名引用Java的ArrayList.java,ArrayList.kt服务于Js模块。
-> 使用平台相关声明,将ArrayList.kt做为预期声明,并在JVM模块、Js模块、Native模块中提供具体的实际声明。使Kotlin对外提供"通用层"API,在不改变代码的状况下,实现跨平台。
当对应单个或多个初始化值的集合时,其使用的都是Java的集合类型,一块儿探究下是否也与平台相关声明有关:
建立单元素集合的listOf(element: T)
、setOf(element: T)
和mapOf(pair: Pair<K, V>)
直接做为顶层函数声明在JVM模块中,并直接使用Java的单元素集合类进行初始化。
#CollectionsJVM.kt
//listOf
public fun <T> listOf(element: T): List<T> =
java.util.Collections.singletonList(element)
复制代码
#SetsJVM.kt
//setOf
public fun <T> setOf(element: T): Set<T> =
java.util.Collections.singleton(element)
复制代码
#MapsJVM.kt
//mapOf
public fun <K, V> mapOf(pair: Pair<K, V>): Map<K, V> =
java.util.Collections.singletonMap(pair.first, pair.second)
复制代码
建立多元素集合的顶层函数的参数都带有vararg
声明,这相似于Java的可变参数,接收任意个数的参数值,并打包为数组。
#Collections.kt
public fun <T> listOf(vararg elements: T): List<T> =
if (elements.size > 0) elements.asList() else emptyList()
复制代码
listOf(vararg elements: T)函数会直接将可变参数转换为list:
#_Arrays.kt
public expect fun <T> Array<out T>.asList(): List<T>
复制代码
Array.asList()拥有 expect 关键字,即做为预期声明存在,这意味着JVM模块会提供对应的实现:
#_ArraysJvm.kt
public actual fun <T> Array<out T>.asList(): List<T> {
return ArraysUtilJVM.asList(this)
}
复制代码
#ArraysUtilJVM.java
class ArraysUtilJVM {
static <T> List<T> asList(T[] array) {
return Arrays.asList(array);
}
}
复制代码
在JVM模块中提供了实际声明的Array.asList()
,并调用了java.util.Arrays.asList()
,返回java.util.Arrays
的静态内部类java.util.Arrays$ArrayList
对象。
#Sets.kt
public fun <T> setOf(vararg elements: T): Set<T> =
if (elements.size > 0) elements.toSet() else emptySet()
复制代码
setOf(vararg elements: T)函数会直接将可变参数转换为set:
public fun <T> Array<out T>.toSet(): Set<T> {
return when (size) {
0 -> emptySet()
1 -> setOf(this[0])
else -> toCollection(LinkedHashSet<T>(mapCapacity(size)))
}
}
复制代码
并和mutableSetOf()
同样,使用Kotlin的LinkedHashSet
依托平台相关声明建立java.util.LinkedHashSet
对象。(具体转换逻辑不深究)
public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> =
if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap()
复制代码
并和mutableMapOf()
同样,使用Kotlin的LinkedHashMap
依托平台相关声明建立java.util.LinkedHashMap
对象。(具体转换逻辑不深究)
了解了一波Kotlin的集合后,须要回归到对集合的使用上——集合的函数式API。
基本定义:
filter函数遍历集合并返回给定lambda中返回true的元素。
源码:
#_Collection.kt
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
//建立一个新的集合并连同lambda一块儿传递给filterTo()
return filterTo(ArrayList<T>(), predicate)
}
public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C {
//遍历原集合
for (element in this)
//执行lambda,如返回为true,则将该元素添加到新集合中
if (predicate(element))
destination.add(element)
//返回新集合
return destination
}
复制代码
解析:
建立一个新的ArrayList
对象,遍历原集合,将lambda表达式返回true的元素添加到新ArrayList
对象中,最后返回新的ArrayList
对象。
基本定义:
map函数对集合中每个元素应用给定的函数,并把结果收集到一个新集合。
源码:
#_Collection.kt
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
//建立一个新的集合并连同lambda一块儿传递给mapTo()
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C {
//遍历旧集合元素
for (item in this)
//执行lambda,对元素进行处理,将返回值添加到新集合中
destination.add(transform(item))
//返回新集合
return destination
}
复制代码
解析:
建立一个新的ArrayList
集合,遍历原集合,将函数类型对象处理过的值添加到新ArrayList
对象中,并返回新的ArrayList
对象。
基本定义:
对集合元素进行分组,并返回一个Map
集合,存储元素分组依据的键和元素分组
源码:
#_Collection.kt
public inline fun <T, K> Iterable<T>.groupBy(keySelector: (T) -> K): Map<K, List<T>> {
//建立一个新的map并连同lambda一块儿传递给groupByTo()
return groupByTo(LinkedHashMap<K, MutableList<T>>(), keySelector)
}
public inline fun <T, K, M : MutableMap<in K, MutableList<T>>> Iterable<T>.groupByTo(destination: M, keySelector: (T) -> K): M {
//遍历旧集合元素
for (element in this) {
//执行lambda,对元素进行处理,将返回值做为key
val key = keySelector(element)
//使用获得的key在新的map中获取vlaue,若是没有则建立一个ArrayList对象,做为value存储到map中,并返回ArrayList对象。
val list = destination.getOrPut(key) { ArrayList<T>() }
//对ArrayList对象添加当前元素
list.add(element)
}
//返回新集合
return destination
}
复制代码
解析:
建立一个LinkedHashMap
对象,遍历旧集合的元素,将函数类型对象处理过的值做为key
,对应的元素存储到一个ArrayList
中,并将该ArrayList
对象做为map
的value
进行存储。返回LinkedHashMap
对象。
基本定义:
根据实参给定的函数对集合中的每一个元素作交换(映射),而后把多个列表平铺成一个列表。
源码:
#_Collection.kt
public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
//建立一个新的集合并连同lambda一块儿传递给flatMapTo()
return flatMapTo(ArrayList<R>(), transform)
}
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {
////遍历旧集合元素
for (element in this) {
//执行lambda,对元素进行处理,返回一个集合
val list = transform(element)
//在获得的集合添加到新的集合中。
destination.addAll(list)
}
//返回新集合
return destination
}
复制代码
解析:
建立一个新的ArrayList
集合,遍历原集合,对原集合的元素转换成列表,最后将转换获得的列表存储到新的ArrayList
集合中,并返回新的ArrayList
对象。
基本定义:
检查集合中的全部元素是否都符合或是否存在符合的元素。
源码:
#_Collection.kt
//any
public inline fun <T> Iterable<T>.any(predicate: (T) -> Boolean): Boolean {
//判断他是否为空,若是集合为空集合,直接返回false,由于确定不存在
if (this is Collection && isEmpty())
return false
for (element in this)
//遍历元素的过程当中,若是有其中一个元素知足条件,则直接返回true
if (predicate(element))
return true
//最后都不行,就返回false
return false
}
//all
public inline fun <T> Iterable<T>.all(predicate: (T) -> Boolean): Boolean {
//若是集合为空集合,直接返回true
if (this is Collection && isEmpty())
return true
for (element in this)
//遍历元素的过程当中,只要有其中一个元素不知足条件,则直接返回false
if (!predicate(element))
return false
return true
}
复制代码
基本定义:
检查有多少知足条件的元素数量。
源码:
#_Collection.kt
public inline fun <T> Iterable<T>.count(predicate: (T) -> Boolean): Int {
if (this is Collection && isEmpty())
return 0
//弄一个临时变量记录数量
var count = 0
//遍历元素
for (element in this)
//若是知足添加,则数量+1
if (predicate(element))
checkCountOverflow(++count)
return count
}
复制代码
基本定义:
寻找第一个符合条件的元素,若是没有符合条件的元素,则返回null
。
源码:
#_Collection.kt
public inline fun <T> Iterable<T>.find(predicate: (T) -> Boolean): T? {
//将lambda传给firstOrNull()
return firstOrNull(predicate)
}
public inline fun <T> Iterable<T>.firstOrNull(predicate: (T) -> Boolean): T? {
for (element in this)
//遍历的元素中,返回第一个符合知足添加的元素。
if (predicate(element))
return element
//没找到,则返回null
return null
}
复制代码
Collection
是全部集合的"基类"Kotlin数组是一个带有类型参数的类,其元素类型被指定为相应的类型参数。
在Kotlin中提供如下方法建立数组:
arrayOf
函数,该函数的实参做为数组的元素。arrayOfNulls
函数,建立一个给定大小的数组,包含的是null值。通常用来建立元素类型可空的数组Array
构造方法,接收一个数组的大小和lambda表达式。lambda表达式用来建立每个数组元素,不能显式地传递每个元素。val array = Array<String>(5){
it.toChar() + "a"
}
复制代码
Kotlin最多见的建立数组的状况是:调用须要数组为参数的Java方法,或调用带有vararg
参数的Kotlin函数。这时须要使用toTypeArray()
将集合转换成数组。
val list = listOf("daqi","java","kotlin")
//集合转数组
list.toTypedArray()
val array = arrayOf("")
//数组转集合
array.toList()
复制代码
Array类的类型参数决定了建立的是一个基本数据类型装箱的数组。当须要建立没有装箱的基本数据类型的数组时,必须使用基本数据类型数组。Kotlin为每一种基本数据类型提供独立的基本数据类型数组。例如:Int
类型的数组叫作IntArray
。基本数据类型数组会被编译成普通的Java基本数据类型的数组,如int[]
.所以基本数据类型数组在存储值时并无装箱。
建立基本数据类型数组:
intArrayOf
)接收变长参数并建立存储这些值的数组。 Kotlin标准库中对集合的支持扩展库(filter
、map
等)同样适用于数组,包括基本数据类型的数组。