Kotlin——高階篇(四):集合(Array、List、Set、Map)基礎

Jetictors發表於2018-03-27

Kotlin——高階篇(四):集合(Array、List、Set、Map)基礎

在實際的專案開發中,集合的運用可以說是多不勝數。不過Kotlin中的集合運用和Java中還是有很大的差別,他們兩者之間,除了集合的型別相同以外,還包含集合的初始化的不同,以及Kotlin對於集合封裝特別多的高階函式以供我們能更簡單、更快捷的編寫程式碼。不過在講解集合之前,我先會對Kotlin中的陣列型別做出一個講解,大家可以當做是對陣列Array<T>的一個溫故。

目錄

Kotlin——高階篇(四):集合(Array、List、Set、Map)基礎

一、陣列型別

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中,集合型別包含三種型別:它們分別是:ListSetMap,他們之間存在以下幾個異同點:
    1. 它們都是介面,並不是實際的類。
    2. 它們只實現了isEmpty()、size、contains()等函式以及屬性。
    3. List<E>Set<E>都繼承至Collection<out E>介面,且Collection<out E>繼承於Iterable<out T>介面。而Map<K,V>是獨立出來的一個介面。這一點和Java相同。
    4. 這三種集合型別分別有存在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>())
}
複製程式碼

SetMap集合的協變和上面的程式碼都相差不多,呼叫不同的轉換函式罷了。除了toMutableList()函式以外,還有著toList()toHashSet()toSet()等等函式。這些函式都是在Iterable介面的擴充函式。大家有興趣可以自己去看看原始碼,這裡不做詳細的累述。

四、一些常用的處理集合型別的擴充函式

除了上面講到的toList()toSet()toHastSet()toMutableList()toSet()toIntArray()等等擴充函式之外。還有一些常用的擴充的高階函式。這裡列舉幾個說明。並例項分析他們的作用。所有的原始碼都在kotlin\collections\_Collections.kt檔案。

不過這裡由於文章篇幅的原因:這一節的內容會在下一章文章講解。

請參見Kotlin——高階篇(五):集合之常用操作符彙總

總結

在這篇文章中,詳細的講解到了集合的幾種型別的宣告與使用,並且也對陣列型別Array<T>溫故了一遍。其實這篇文章的內容並不是很多,大家主要記住集合型別初始化的幾個標準函式,以及集合的型別協變。在下一篇文章中會對處理集合與陣列的常見函式做出一個講解以及原始碼的剖析。

原始碼

在這最後希望您能給個關注,因為您的關注,是我繼續寫文章最好的動力。

我的個人部落格Jetictors
GithubJteictors
我的掘金Jetictors

歡迎各位大佬進群共同研究、探索

QQ群號:497071402

Kotlin——高階篇(四):集合(Array、List、Set、Map)基礎

相關文章