在實際的專案開發中,集合的運用可以說是多不勝數。不過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
,他們之間存在以下幾個異同點:
- 它們都是介面,並不是實際的類。
- 它們只實現了
isEmpty()、size、contains()
等函式以及屬性。List<E>
和Set<E>
都繼承至Collection<out E>
介面,且Collection<out E>
繼承於Iterable<out T>
介面。而Map<K,V>
是獨立出來的一個介面。這一點和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>
溫故了一遍。其實這篇文章的內容並不是很多,大家主要記住集合型別初始化的幾個標準函式,以及集合的型別協變。在下一篇文章中會對處理集合與陣列的常見函式做出一個講解以及原始碼的剖析。
在這最後希望您能給個關注,因為您的關注,是我繼續寫文章最好的動力。
我的個人部落格:Jetictors
Github:Jteictors
我的掘金:Jetictors