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
等)同樣適用於數組,包括基本數據類型的數組。