在上一篇文章Kotlin——高階篇(四):集合(Array、List、Set、Map)基礎中講解到了陣列Array<T>
、集合(List
、Set
、Map
)的定義與初始化。但是由於篇幅的原因,未講解到操作他們的常用高階函式。故而今天這篇文章詳細的講解這些函式。對他們的作用進行剖。並例項講解他們的應用場景。當看完這篇文章,我相信你能對集合使用的得心應手。
目錄
一、轉換類
在上一篇文章中講解陣列(Array<T>
)的與集合的時候也提到了關於集合轉換的高階函式。下面用原始碼的角度去分析並用例項去講解其轉換的過程。
1.1、轉換為陣列
當我們宣告一個集合,可以把這個集合根據呼叫集合類相應的高階函式來轉換成相應的陣列。集合類提供了
toIntArray()
、toDoubleArray()
、toFloatArray()
、toBetArray()
等高階函式去處理。下面提供一個函式的原始碼,其他函式的原始碼處理邏輯是相同的,有興趣的朋友可以去看看這個原始碼類。
原始碼:
public fun Collection<Int>.toIntArray(): IntArray {
val result = IntArray(size)
var index = 0
for (element in this)
result[index++] = element
return result
}
複製程式碼
解釋:從上面的原始碼,我們可以看出,該函式的返回型別時IntArray
型別,即表示一個Int
型別的陣列。該函式的邏輯是定義一個Int
型別的陣列,然後遍歷集合,把集合的元素一個一個的新增到定義的陣列當中。
例項:
fun listToArray(){
val list = listOf<Int>(1,2,3,4,5,6) // 宣告一個Int型別的List
val listArray = list.toIntArray() // 轉換
println(list.javaClass.toString()) // 列印list的型別
println(listArray.javaClass.toString()) // 列印listArray的型別
println(listArray[1])
}
複製程式碼
輸出結果:
變數list的型別為:class java.util.LinkedHashSet
變數listArray的型別為:class [I
2
複製程式碼
1.2、轉換為集合
我們知道在
Kotlin
中,集合可分為不可變集合與可變集合。我們宣告一個集合或者陣列,可以轉換成相應型別的集合。呼叫toXXX()
轉換成不可變集合。呼叫toMutableXXX()
轉換為可變集合。集合類提供了toList()
、toMutableList()
、toSet()
、toMutableSet()
、toHashSet()
、toMap()
等高階函式去處理。同理也是從原始碼的角度去分析。
原始碼:這個是集合轉集合的原始碼。陣列轉集合的原始碼還要簡單些。
public fun <T> Iterable<T>.toList(): List<T> {
if (this is Collection) {
return when (size) {
0 -> emptyList()
1 -> listOf(if (this is List) get(0) else iterator().next())
else -> this.toMutableList()
}
}
return this.toMutableList().optimizeReadOnlyList()
}
複製程式碼
解釋:首先判斷是不是一個集合,如果是一個集合的情況,則根據集合的元素個數來執行相應的邏輯,當元素的個數為0
時,返回一個空集合。當為1
個的時候,用listOf()
去初始化一個List
集合。在這個新集合中去這個判斷原集合的型別是不是返回集合的型別,如果是,則獲取原集合的第一個元素作為新集合的元素返回。反之,則遍歷原集合的元素。當原集合個數不為0
或1
時,使用toMutableList()
轉換成list。如果不是集合,則直接使用toMutableList()
轉換。這裡的optimizeReadOnlyList()
函式的邏輯即是上面原集合的邏輯。
解釋起來很複雜,還是用程式碼說話吧...
例項:
// 陣列轉集合
fun arrayToList() {
val arr = arrayOf(1,3,5,7,9)
val list = arr.toList()
println("變數arr的型別為:${arr.javaClass}")
println("變數list的型別為:${list.javaClass}")
println(list[1])
}
// 集合轉集合,這裡用Set轉List
fun listToList(){
val set = setOf(1)
val setTolist = set.toList()
println("變數set的型別為:${set.javaClass}")
println("變數setTolist的型別為:${setTolist.javaClass}")
println(setTolist[0])
}
複製程式碼
輸出結果為:
變數arr的型別為:class [Ljava.lang.Integer;
變數list的型別為:class java.util.ArrayList
3
變數set的型別為:class java.util.Collections$SingletonSet
變數setTolist的型別為:class java.util.Collections$SingletonList
1
複製程式碼
關於集合轉換的問題就講到這裡,有興趣的同學可以去看看原始碼的實現。
二、操作類
關於集合操作類的函式大致分為六類,他們幾乎上囊括了原始碼中實現的操作方法。下面對這6
類操作符進行一一的講解,不過這裡就不對他們的原始碼進行分析了。大家有興趣的話可以去看看他們的原始碼實現。幾乎上都存在於_Collections.kt
這個檔案中
2.1、元素操作符
元素操作符在這裡包括:
contains(元素)
: 檢查集合中是否包含指定的元素,若存在則返回true
,反之返回false
elementAt(index)
: 獲取對應下標的元素。若下標越界,會丟擲IndexOutOfBoundsException(下標越界)
異常,同get(index)
一樣elementAtOrElse(index,{...})
: 獲取對應下標的元素。若下標越界,返回預設值,此預設值就是你傳入的下標的運算值elementAtOrNull(index)
: 獲取對應下標的元素。若下標越界,返回null
first()
: 獲取第一個元素,若集合為空集合,這會丟擲NoSuchElementException
異常first{}
: 獲取指定元素的第一個元素。若不滿足條件,則丟擲NoSuchElementException
異常firstOrNull()
: 獲取第一個元素,若集合為空集合,返回null
firstOrNull{}
: 獲取指定元素的第一個元素。若不滿足條件,返回null
getOrElse(index,{...})
: 同elementAtOrElse
一樣getOrNull(index)
: 同elementAtOrNull
一樣last()
: 同first()
相反last{}
: 同first{}
相反lastOrNull{}
: 同firstOrNull()
相反lastOrNull()
: 同firstOrNull{}
相反indexOf(元素)
: 返回指定元素的下標,若不存在,則返回-1
indexOfFirst{...}
: 返回第一個滿足條件元素的下標,若不存在,則返回-1
indexOfLast{...}
: 返回最後一個滿足條件元素的下標,若不存在,則返回-1
single()
: 若集合的長度等於0
,則丟擲NoSuchElementException
異常,若等於1
,則返回第一個元素。反之,則丟擲IllegalArgumentException
異常single{}
: 找到集合中滿足條件的元素,若元素滿足條件,則返回該元素。否則會根據不同的條件,丟擲異常。這個方法慎用singleOrNull()
: 若集合的長度等於1
,則返回第一個元素。否則,返回null
singleOrNull{}
: 找到集合中滿足條件的元素,若元素滿足條件,則返回該元素。否則返回null
forEach{...}
: 遍歷元素。一般用作元素的列印forEachIndexed{index,value}
: 遍歷元素,可獲得集合中元素的下標。一般用作元素以及下標的列印componentX()
: 這個函式在前面的章節中提過多次了。用於獲取元素。其中的X
只能代表1..5
。詳情可看下面的例子
例:
val list = listOf("kotlin","Android","Java","PHP","Python","IOS")
println(" ------ contains -------")
println(list.contains("JS"))
println(" ------ elementAt -------")
println(list.elementAt(2))
println(list.elementAtOrElse(10,{it}))
println(list.elementAtOrNull(10))
println(" ------ get -------")
println(list.get(2))
println(list.getOrElse(10,{it}))
println(list.getOrNull(10))
println(" ------ first -------")
println(list.first())
println(list.first{ it == "Android" })
println(list.firstOrNull())
println(list.firstOrNull { it == "Android" })
println(" ------ last -------")
println(list.last())
println(list.last{ it == "Android" })
println(list.lastOrNull())
println(list.lastOrNull { it == "Android" })
println(" ------ indexOf -------")
println(list.indexOf("Android"))
println(list.indexOfFirst { it == "Android" })
println(list.indexOfLast { it == "Android" })
println(" ------ single -------")
val list2 = listOf("list")
println(list2.single()) // 只有當集合只有一個元素時,才去用這個函式,不然都會丟擲異常。
println(list2.single { it == "list" }) //當集合中的元素滿足條件時,才去用這個函式,不然都會丟擲異常。若滿足條件返回該元素
println(list2.singleOrNull()) // 只有當集合只有一個元素時,才去用這個函式,不然都會返回null。
println(list2.singleOrNull { it == "list" }) //當集合中的元素滿足條件時,才去用這個函式,不然返回null。若滿足條件返回該元素
println(" ------ forEach -------")
list.forEach { println(it) }
list.forEachIndexed { index, it -> println("index : $index \t value = $it") }
println(" ------ componentX -------")
println(list.component1()) // 等價於`list[0] <=> list.get(0)`
println(list.component2()) // 等價於`list[1] <=> list.get(1)`
println(list.component3()) // 等價於`list[2] <=> list.get(2)`
println(list.component4()) // 等價於`list[3] <=> list.get(3)`
println(list.component5()) // 等價於`list[4] <=> list.get(4)`
複製程式碼
輸出結果為:
------ contains -------
false
------ elementAt -------
Java
10
null
------ get -------
Java
10
null
------ first -------
kotlin
Android
kotlin
Android
------ last -------
IOS
Android
IOS
Android
------ indexOf -------
1
1
1
------ single -------
list
list
list
list
------ forEach -------
kotlin
Android
Java
PHP
Python
IOS
index : 0 value = kotlin
index : 1 value = Android
index : 2 value = Java
index : 3 value = PHP
index : 4 value = Python
index : 5 value = IOS
------ componentX -------
kotlin
Android
Java
PHP
Python
複製程式碼
2.2、順序操作符
順序操作符包括:
reversed()
: 反序。即和初始化的順序反過來。sorted()
: 自然升序。sortedBy{}
: 根據條件升序,即把不滿足條件的放在前面,滿足條件的放在後面sortedDescending()
: 自然降序。sortedByDescending{}
: 根據條件降序。和sortedBy{}
相反
其中從原始碼的角度說:
sorted()
函式的核心是sort()
函式。但這個sort()
函式我不能直接呼叫罷了。reversed()
函式呼叫的是Java
中的reversed()
函式。- 其餘的
3
個函式實現的核心為sortWith()
函式。故而我們也可以用sortWith()
來實現其他的排序方式
例:
val list1 = listOf(-1,-3,1,3,5,6,7,2,4,10,9,8)
// 反序
println(list1.reversed())
// 升序
println(list1.sorted())
// 根據條件升序,即把不滿足條件的放在前面,滿足條件的放在後面
println(list1.sortedBy { it % 2 == 0})
// 降序
println(list1.sortedDescending())
// 根據條件降序,和`sortedBy{}`相反
println(list1.sortedByDescending { it % 2 == 0 })
複製程式碼
輸出結果為:
[8, 9, 10, 4, 2, 7, 6, 5, 3, 1, -3, -1]
[-3, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[-1, -3, 1, 3, 5, 7, 9, 6, 2, 4, 10, 8]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, -1, -3]
[6, 2, 4, 10, 8, -1, -3, 1, 3, 5, 7, 9]
複製程式碼
2.3、對映操作符
對映操作符包括:
map{...}
: 把每個元素按照特定的方法進行轉換,組成一個新的集合。mapNotNull{...}
: 同map{}
函式的作用相同,只是過濾掉轉換之後為null
的元素mapIndexed{index,result}
: 把每個元素按照特定的方法進行轉換,只是其可以操作元素的下標(index
),組成一個新的集合。mapIndexedNotNull{index,result}
: 同mapIndexed{}
函式的作用相同,只是過濾掉轉換之後為null
的元素flatMap{...}
: 根據條件合併兩個集合,組成一個新的集合。groupBy{...}
: 分組。即根據條件把集合拆分為為一個Map<K,List<T>>
型別的集合。具體看例項
例:
val list1 = listOf("kotlin","Android","Java","PHP","JavaScript")
println(list1.map { "str-".plus(it) })
println(list1.mapNotNull { "str-".plus(it) })
println(list1.mapIndexed { index, str ->
index.toString().plus("-").plus(str)
})
println(list1.mapIndexedNotNull { index, str ->
index.toString().plus("-").plus(str)
})
println( list1.flatMap { listOf(it,"new-".plus(it)) })
println(list1.groupBy { if (it.startsWith("Java")) "big" else "latter" })
複製程式碼
輸出結果為:
[str-kotlin, str-Android, str-Java, str-PHP, str-JavaScript]
[str-kotlin, str-Android, str-Java, str-PHP, str-JavaScript]
[0-kotlin, 1-Android, 2-Java, 3-PHP, 4-JavaScript]
[0-kotlin, 1-Android, 2-Java, 3-PHP, 4-JavaScript]
[kotlin, new-kotlin, Android, new-Android, Java, new-Java, PHP, new-PHP, JavaScript, new-JavaScript]
{latter=[kotlin, Android, PHP], big=[Java, JavaScript]}
複製程式碼
2.4、過濾操作符
過濾操作符包括:
filter{...}
: 把不滿足條件的元素過濾掉filterIndexed{...}
: 和filter{}
函式作用類似,只是可以操作集合中元素的下標(index
)filterNot{...}
: 和filter{}
函式的作用相反filterNotNull()
: 過濾掉集合中為null
的元素。take(num)
: 返回集合中前num
個元素組成的集合takeWhile{...}
: 迴圈遍歷集合,從第一個元素開始遍歷集合,當第一個出現不滿足條件元素的時候,退出遍歷。然後把滿足條件所有元素組成的集合返回。takeLast(num)
: 返回集合中後num
個元素組成的集合takeLastWhile{...}
: 迴圈遍歷集合,從最後一個元素開始遍歷集合,當第一個出現不滿足條件元素的時候,退出遍歷。然後把滿足條件所有元素組成的集合返回。drop(num)
: 過濾集合中前num
個元素dropWhile{...}
: 相同條件下,和執行takeWhile{...}
函式後得到的結果相反dropLast(num)
: 過濾集合中後num
個元素dropLastWhile{...}
: 相同條件下,和執行takeLastWhile{...}
函式後得到的結果相反distinct()
: 去除重複元素distinctBy{...}
: 根據操作元素後的結果去除重複元素slice
: 過濾掉所有不滿足執行下標的元素。
例:
val list1 = listOf(-1,-3,1,3,5,6,7,2,4,10,9,8)
val list2 = listOf(1,3,4,5,null,6,null,10)
val list3 = listOf(1,1,5,2,2,6,3,3,7,4,4,8)
println(" ------ filter -------")
println(list1.filter { it > 1 })
println(list1.filterIndexed { index, result ->
index < 5 && result > 3
})
println(list1.filterNot { it > 1 })
println(list2.filterNotNull())
println(" ------ take -------")
println(list1.take(5))
println(list1.takeWhile { it < 5 })
println(list1.takeLast(5))
println(list1.takeLastWhile { it > 5 })
println(" ------ drop -------")
println(list1.drop(5))
println(list1.dropWhile { it < 5 })
println(list1.dropLast(5))
println(list1.dropLastWhile { it > 5 })
println(" ------ distinct -------")
println(list3.distinct())
println(list3.distinctBy { it + 2 })
println(" ------ slice -------")
println(list1.slice(listOf(1,3,5,7)))
println(list1.slice(IntRange(1,5)))
複製程式碼
輸出結果為:
------ filter -------
[3, 5, 6, 7, 2, 4, 10, 9, 8]
[5]
[-1, -3, 1]
[1, 3, 4, 5, 6, 10]
------ take -------
[-1, -3, 1, 3, 5]
[-1, -3, 1, 3]
[2, 4, 10, 9, 8]
[10, 9, 8]
------ drop -------
[6, 7, 2, 4, 10, 9, 8]
[5, 6, 7, 2, 4, 10, 9, 8]
[-1, -3, 1, 3, 5, 6, 7]
[-1, -3, 1, 3, 5, 6, 7, 2, 4]
------ distinct -------
[1, 5, 2, 6, 3, 7, 4, 8]
[1, 5, 2, 6, 3, 7, 4, 8]
------ slice -------
[-3, 3, 6, 2]
[-3, 1, 3, 5, 6]
複製程式碼
2.5、生產操作符
生產操作符包括:
plus()
: 合併兩個集合中的元素,組成一個新的集合。也可以使用符號+
zip
: 由兩個集合按照相同的下標組成一個新集合。該新集合的型別是:List<Pair>
unzip
: 和zip
的作用相反。把一個型別為List<Pair>
的集合拆分為兩個集合。看下面的例子partition
: 判斷元素是否滿足條件把集合拆分為有兩個Pair
組成的新集合。
例:
val list1 = listOf(1,2,3,4)
val list2 = listOf("kotlin","Android","Java","PHP","JavaScript")
// plus() 和 `+`一樣
println(list1.plus(list2))
println(list1 + list2)
// zip
println(list1.zip(list2))
println(list1.zip(list2){ // 組成的新集合由元素少的原集合決定
it1,it2-> it1.toString().plus("-").plus(it2)
})
// unzip
val newList = listOf(Pair(1,"Kotlin"),Pair(2,"Android"),Pair(3,"Java"),Pair(4,"PHP"))
println(newList.unzip())
// partition
println(list2.partition { it.startsWith("Ja") })
複製程式碼
輸出結果為:
[1, 2, 3, 4, kotlin, Android, Java, PHP, JavaScript]
[1, 2, 3, 4, kotlin, Android, Java, PHP, JavaScript]
[(1, kotlin), (2, Android), (3, Java), (4, PHP)]
[1-kotlin, 2-Android, 3-Java, 4-PHP]
([1, 2, 3, 4], [Kotlin, Android, Java, PHP])
([Java, JavaScript], [kotlin, Android, PHP])
複製程式碼
2.6、統計操作符
統計操作符包括:
any()
: 判斷是不是一個集合,若是,則在判斷集合是否為空,若為空則返回false
,反之返回true,若不是集合,則返回hasNext
any{...}
: 判斷集合中是否存在滿足條件的元素。若存在則返回true
,反之返回false
all{...}
: 判斷集合中的所有元素是否都滿足條件。若是則返回true
,反之則返回false
none()
: 和any()
函式的作用相反none{...}
: 和all{...}
函式的作用相反max()
: 獲取集合中最大的元素,若為空元素集合,則返回null
maxBy{...}
: 獲取方法處理後返回結果最大值對應那個元素的初始值,如果沒有則返回null
min()
: 獲取集合中最小的元素,若為空元素集合,則返回null
minBy{...}
: 獲取方法處理後返回結果最小值對應那個元素的初始值,如果沒有則返回null
sum()
: 計算出集合元素累加的結果。sumBy{...}
: 根據元素運算操作後的結果,然後根據這個結果計算出累加的值。sumByDouble{...}
: 和sumBy{}
相似,不過sumBy{}
是操作Int
型別資料,而sumByDouble{}
操作的是Double
型別資料average()
: 獲取平均數reduce{...}
: 從集合中的第一項到最後一項的累計操作。reduceIndexed{...}
: 和reduce{}
作用相同,只是其可以操作元素的下標(index
)reduceRight{...}
: 從集合中的最後一項到第一項的累計操作。reduceRightIndexed{...}
: 和reduceRight{}
作用相同,只是其可以操作元素的下標(index
)fold{...}
: 和reduce{}
類似,但是fold{}
有一個初始值foldIndexed{...}
: 和reduceIndexed{}
類似,但是foldIndexed{}
有一個初始值foldRight{...}
: 和reduceRight{}
類似,但是foldRight{}
有一個初始值foldRightIndexed{...}
: 和reduceRightIndexed{}
類似,但是foldRightIndexed{}
有一個初始值
例:
val list1 = listOf(1,2,3,4,5)
println(" ------ any -------")
println(list1.any())
println(list1.any{it > 10})
println(" ------ all -------")
println(list1.all { it > 2 })
println(" ------ none -------")
println(list1.none())
println(list1.none{ it > 2})
println(" ------ max -------")
println(list1.max())
println(list1.maxBy { it + 2 })
println(" ------ min -------")
println(list1.min()) // 返回集合中最小的元素
println(list1.minBy { it + 2 })
println(" ------ sum -------")
println(list1.sum())
println(list1.sumBy { it + 2 })
println(list1.sumByDouble { it.toDouble() })
println(" ------ average -----")
println(list1.average())
println(" ------ reduce -------")
println(list1.reduce { result, next -> result + next})
println(list1.reduceIndexed { index, result, next ->
index + result + next
})
println(list1.reduceRight { result, next -> result + next })
println(list1.reduceRightIndexed {index, result, next ->
index + result + next
})
println(" ------ fold -------")
println(list1.fold(3){result, next -> result + next})
println(list1.foldIndexed(3){index,result, next ->
index + result + next
})
println(list1.foldRight(3){result, next -> result + next})
println(list1.foldRightIndexed(3){index,result, next ->
index + result + next
})
複製程式碼
輸出結果為:
------ any -------
true
false
------ all -------
false
------ none -------
false
false
------ max -------
5
5
------ min -------
1
1
------ sum -------
15
25
15.0
------ average -----
3.0
------ reduce -------
15
25
15
21
------ fold -------
18
28
18
28
複製程式碼
總結
這篇文章篇幅很長,同時內容也很多。對於操作符講解的不是很全,主要是介紹常用的操作符。並且對他們的含義即實現進行講解。但是我相信您學習完上面的內容,對於集合的操作應該是得心印手了。同時瞭解了這些操作符是怎麼實現的。
關於Kotlin
中對於集合的操作符,我推薦幾篇別人的文章:
Kotlin系列之常用操作符
Kotlin入門第二課:集合操作
Kotlin集合與它的操作符們
上面幾篇文章對於Kotlin
的全部操作符的介紹與使用講解的很全面。並且都有例子去實現。
在這最後希望您能給個關注,因為您的關注,是我繼續寫文章最好的動力。
我的個人部落格:Jetictors
Github:Jteictors