在實際的項目開發中,集合的運用可以說是多不勝數。不過Kotlin
中的集合運用和Java
中還是有很大的差別,他們兩者之間,除了集合的類型相同以外,還包含集合的初始化的不同,以及Kotlin
對於集合封裝特別多的高階函數以供我們能更簡單、更快捷的編寫代碼。不過在講解集合之前,我先會對Kotlin
中的數組類型
做出一個講解,大家可以當做是對數組Array<T>
的一個溫故。
目錄
一、數組類型
在
Kotlin
數組類型不是集合中的一種,但是它又和集合有着太多相似的地方。並且數組和集合可以互換。並且在初始化集合的時候也可以傳入一個數組。用於數組類型在前面的章節已經講解過了,這里就不在多做累述。有興趣的朋友可以去看我前面關於數據類型的文章。
Kotlin——初級篇(三):數據類型詳解
這里只介紹幾個常用的方法。其實在文章的后面,也對這些方法做出了講解。
- 用
arr[index]
的獲取元素。 - 用
arr.component1() ... arr.component5()
獲取數組的前5個元素。同樣適用與集合。 - 用
arr.reverse()
反轉元素。 - 至於其他處理的元素,在文章的尾部都有說明。這也是我把數組類型與集合類型放在同一文章講解的原因。
例1:使用componentX()
函數
val arr = arrayOf("1",2,3,4)
println(arr.component1())
println(arr.component3())
// 程序崩潰,因為元素只有4個,所以在不確定元素個數的情況,慎用這些函數,還是使用遍歷安全些。
println(arr.component5())
輸出結果:
1
3
例2 :反轉元素
val arr = arrayOf("1",2,3,4)
arr.reverse()
// 文章后面會講解forEach高階函數。比for循環簡潔多了
for (index in arr){
print("$index \t")
}
輸出結果:
4 3 2 1
二、集合類型
Kotlin
中的集合和其他語言不同的是,Kotlin
集合可分為可變和不可變集合。- 在
Kotlin
中,集合類型包含三種類型:它們分別是:List
、Set
、Map
,這三種類型都有幾個共通點:
- 它們都是接口,並不是實際的類。
- 它們都繼承至
Collection<out E>
接口,而Collection<out E>
又繼承與Iterable<out T>
接口。它們幾乎上只實現了isEmpty()、size屬性、get()、contains()
等方法。這一點和Java
類似。- 這三種集合類型分別有存在
MutableList<E>、MutableSet<E>、MutableMap<K,V>接口
,這些接口中提供了改變、操作集合的方法。例如add()
、clear()
、remove()
等函數。
有以上三點我們可出,在定義集合類型變量的時候如果使用List<E>
、Set<E>
、Map<K,V>
聲明的時候該集合則是不可變集合,而使用MutableList<E>
、MutableSet<E>
、MutableMap<K,V>
的時候該集合才是可變類型集合。這里我就不提供源碼來分析了,有興趣的可以看一看源碼!源碼在kotlin\collections\Collections.kt
文件
下面對幾個集合類型進行一一的講解。
2.1、List類型
我們知道,一個接口是不能直接實例化的,那我們要初始化一個怎么做呢?其實Kotlin
給我們提供了相應的標准庫函數去處理。
- 聲明並初始化List
的集合:使用 listOf(..)
函數- 聲明並初始化MutableList
的集合:使用 mutableListOf(..)
函數
例1:使用listOf()
初始化不可變的List類型
集合
val arr = arrayOf("1","2",3,4,5)
val list1 = listOf(1,2,"3",4,"5") // 隨意創建
val list2 = listOf<String>("1","2","3","4","5") // 確定元素的值類型
val list3 = listOf(arr) // 可傳入一個數組
以下代碼是錯誤的。因為List<E>只能是不可變集合。而add、remove、clear等函數時MutableList中的函數
list1.add()
list1.remove
...
// 遍歷
for(value in list1){
print("$value \t")
}
輸出結果:
1 2 3 4 5
例2:使用mutableListOf()
初始化不可變的List類型
集合
val arr = arrayOf("1",2,3,4)
val mutableList1 = mutableListOf(1,2,"3",4,"5") // 隨意創建
val mutableList2 = mutableListOf<String>("1","2","3","4","5") // 確定元素的值類型
val mutableList3 = mutableListOf(arr) // 可傳入一個數組
val mutableList : ArrayList<String> // 這里的ArrayList<>和Java里面的ArrayList一致
mutableList1.add("6") // 添加元素
mutableList1.add("7")
mutableList1.remove(1) // 刪除某一元素
// 遍歷
for(value in mutableList1){
print("$value \t")
}
mutableList1.clear() // 清空集合
輸出結果為:
2 3 4 5 6 7
2.2、Set類型
Set類型
集合的使用和List類型
集合大致相同。這里不做詳細的介紹,只講解它和List類型
集合不同的地方。
- 聲明並初始化Set
的集合:使用 setOf(..)
函數- 聲明並初始化MutableSet
的集合:使用 mutableSetOf(..)
函數
例1: 聲明並初始化
val set1 = setOf(1,2,"3","4","2",1,2,3,4,5)
val mutableSet1 = mutableSetOf(1,2,"3","4","2",1,2,3,4,5)
val mutableSet2 : HashSet<String> // 這里的HashSet<>和Java里面的HashSet<>一致
例2 :遍歷集合,看效果與預計的有什么不同
// 遍歷
for(value in set1){
print("$value \t")
}
輸出結果:
1 2 3 4 2 3 4 5
在我們預計的效果中,遍歷的結果應該為:1 2 3 4 2 1 2 3 4 5
,但是結果卻少了一個1 2
。那么我們可以看出,Set類型
集合會把重復的元素去除掉。這一點和Java
是不謀而合的。這個特性也是Set類型
集合與List集合
類型的區別所在。
2.3、Map類型
Map<K,V>類型
集合和List
以及Set
都有着差別。下面我們看Map類型
集合的聲明及初始化。
同前面兩種類型一樣,Map
同樣也分為不可變與可變集合。其中:
- 不可變的
Map類型
集合的初始化使用:mapOf()
函數- 可變的
Map類型
集合的初始化使用:mutableMapOf()
函數
不過初始化和前面兩種類型有差別,Map集合
類型是一種以鍵-值
對的方式出現。例:
// 以鍵值對的形式出現,鍵與值之間使用to
val map1 = mapOf("key1" to 2 , "key2" to 3)
val map2 = mapOf<Int,String>(1 to "value1" , 2 to "value2")
val mutableMap = mutableMapOf("key1" to 2 , "key1" to 3)
val hashMap = hashMapOf("key1" to 2 , "key1" to 3) // 同Java中的HashMap
map2.forEach{
key,value -> println("$key \t $value")
}
輸出結果為:
1 value1
2 value2
注意:當我們的鍵存在重復時,集合會過濾掉之前重復的元素。
例:
val map = val map1 = mapOf("key1" to 2 , "key1" to 3 , "key1" to "value1" , "key2" to "value2")
map.forEach{
key,value -> println("$key \t $value")
}
輸出結果為:
key1 value1
key2 value2
從上面的例子可以看出,當key
值為key1
時,元素只保留了最后一個元素。而過濾掉了之前key
值相同的所有元素。
三、 集合類型的協變
試想一下,當一個集合賦值給另外一個集合時,這里以List<E>
舉例,如果兩個集合的類型也就是E
類型相同時,賦值是沒有問題的。如果類型不同的情況,當E
繼承自M
時。你就可以把List<E>
賦值給List<M>
了。這種情況稱之為協變
我這里舉兩個例子
例1:
open class Person(val name : String , val age : Int){
override fun toString(): String {
return "Person(name='$name', age=$age)"
}
}
class Student(name: String, age : Int, cls : String) : Person(name, age)
// 注意:Any是kotlin中的超類,故而Student類也是繼承自Any的。這里你可以換成Person類結果是相同的
var listPerson: List<Any>
val listStudent : List<Student> = listOf(Student("張三",12,"一班"),Student("王五",20,"二班"))
listPerson = listStudent
listPerson.forEach { println(it.toString()) }
輸出結果:
Person(name='張三', age=12)
Person(name='王五', age=20)
例2:當集合的類型相同或有繼承關系時,一個集合使用MutableList
,一個集合使用List
的情況。
var mutableListPerson: MutableList<Person>
val mutableListStudent : List<Student> = listOf(Student("張三",12,"一班"),Student("王五",20,"二班"))
mutableListPerson = mutableListStudent.toMutableList()
mutableListPerson.add(Person("a",15))
mutableListPerson.add(Person("b",45))
mutableListPerson.forEach { println(it.toString()) }
輸出結果為:
Person(name='張三', age=12)
Person(name='王五', age=20)
Person(name='a', age=15)
Person(name='b', age=45)
看上面的實例2,使用了一個toMutableList()
函數,其實這個函數的意思是把List
轉換成了MutableList
。在以下的源碼中我們可以看出:其實是實例化了一個ArrayList
。
public fun <T> Collection<T>.toMutableList(): MutableList<T> {
return ArrayList(this)
}
public fun <T> Iterable<T>.toMutableList(): MutableList<T> {
if (this is Collection<T>)
return this.toMutableList()
return toCollection(ArrayList<T>())
}
Set
、Map
集合的協變和上面的代碼都相差不多,調用不同的轉換函數罷了。除了toMutableList()
函數以外,還有着toList()
、toHashSet()
、toSet()
等等函數。這些函數都是在Iterable
接口的拓展函數。大家有興趣可以自己去看看源碼,這里不做詳細的累述。
四、一些常用的處理集合類型的拓展函數
除了上面講到的toList()
、toSet()
、toHastSet()
、toMutableList()
、toSet()
、toIntArray()
等等拓展函數之外。還有一些常用的拓展的高階函數。這里列舉幾個說明。並實例分析他們的作用。所有的源碼都在kotlin\collections\_Collections.kt
文件。
不過這里由於文章篇幅的原因:這一節的內容會在下一章文章講解。
總結
在這篇文章中,詳細的講解到了集合的幾種類型的聲明與使用,並且也對數組類型Array<T>
溫故了一遍。其實這篇文章的內容並不是很多,大家主要記住集合類型初始化的幾個標准函數,以及集合的類型協變。在下一篇文章中會對處理集合與數組的常見函數做出一個講解以及源碼的剖析。
如果各位大佬看了之后感覺還闊以,就請各位大佬隨便star
一下,您的關注是我最大的動力。
我的個人博客:Jetictors
Github:Jteictors
掘金:Jteictors