Kotlin極簡教程:第5章 集合類

原文鏈接:https://github.com/EasyKotlin

本章將介紹Kotlin標準庫中的集合類,我們將瞭解到它是如何擴展的Java集合庫,使得寫代碼更加簡單容易。如果您熟悉Scala的集合庫,您會發現Kotlin跟Scala集合類庫的相似之處。

5.1 集合類是什麼

5.1.2 集合類是一種數據結構

在講 Kotlin 的集合類之前,爲了更加深刻理解爲什麼要有集合類,以及集合類到底是怎麼一回事,讓我們先來簡單回顧一下編程的本質:

數據結構 + 算法 (信息的邏輯結構及其基本操作)

我們使用計算機編程來解決一個具體問題時,大致需要經過下列幾個步驟:

首先要從具體問題中抽象出一個適當的數學模型;
然後設計一個解此數學模型的算法(Algorithm);
最後編出程序、進行測試、修改直至得到最終解答。

這裏面的尋求數學模型的過程,實質就是分析問題,從中提取操作的對象,並找出這些操作對象之間含有的關係的過程。建立好的模型,我們使用數學語言來表達。

這裏的模型對應的就是數據結構。我們用計算機編程來解決問題的關鍵就是,設計出合適的數據結構(例如,用線性表、樹、圖等)和性能良好的算法。

算法與數據的結構密切相關,算法無不依附於具體的數據結構,數據結構直接關係到算法的選擇和效率。通常情況下,設計良好的數據結構可以大大簡化算法的實現複雜度,同時可以提升存儲效率。數據結構往往同高效的檢索算法和索引技術相關。

我們可以把數據結構理解爲是ADT的實現。數據結構就是現實問題模型的表達。

數據結構主要解決以下三個問題:

  • 數據元素之間的邏輯關係。

這些邏輯關係有:集合、線性結構、樹形結構、圖形結構等。

  • 數據的物理結構。

數據的邏輯結構在計算機存儲空間的存放形式。數據的物理結構是數據結構在計算機中的映射。其具體實現的方法有: 順序(Sequence)、鏈接(Link)、索引(Index)、散列(Hash)等形式。

其中,順序存儲結構和鏈式存儲結構是我們常用的兩種存儲結構。

順序存儲是使用元素在存儲器中的相對位置來表示數據元素之間的邏輯關係;

鏈式存儲使用指示元素存儲位置的指針(pointer)來表示數據元素之間的邏輯關係。

  • 數據的處理運算。

5.1.2 集合類是SDK API

我們現在很少用抽象數據類型ADT(Abstract Data Type)這個概念,其實這個概念是OO範式的前身,也是類的前身。ADT加上繼承、重載和多態性就是現代OOP編程範式中的類的概念了。我們簡稱類爲廣義ADT的概念。

如果我們更加廣義的來理解這裏的ADT的思想,其實各種編程語言的SDK API、所有的服務(IaaS,PaaS和SaaS等)都是一種更加廣義的ADT。

使用ADT可以讓我們更簡單地描述現實世界。例如:用線性表描述學生成績表,用樹或圖描述遺傳關係等。

我們知道類的本質就是,對象及其關係的抽象(abstraction)。一個類通常有屬性(數據結構)和行爲(算法)。使用OO範式編程的大致過程爲:

劃分對象 → 抽象類 → 將類組織成爲層次化結構(繼承和合成) → 用類與實例進行設計和實現

等幾個階段。

數據抽象本質上講就是我們解決現實問題的過程中,進行建立領域模型(Domain Model)的過程。

比如說,在前一章節中,我們介紹的程序設計語言的類型系統,本質上就是一種數據抽象。由於計算機的結構和存儲的限制(無法像人類大腦神經系統一樣去認知識別,並解決現實問題),人類大腦在解決實際問題過程中,經常要計算整數、小數, 要處理英文字符、中文字符, 要持有對象(被操作的數據),要對這些對象進行諸如:查找、排序、修改、傳遞等操作。把這些問題解決中最常用的數據結構以及其操作算法抽象成對應的類(例如:String、Array、List、Set、Map等),這樣我們就可以極大的複用這些功能。而不需要我們自己來實現諸如:字符串、數組、列表、集合、映射等這些的數據結構。通常這些最通用的數據結構,都是現在編程語言中內置的了。

5.1.3 連續存儲和離散存儲

內存中的存儲形式可以分爲連續存儲和離散存儲兩種。因此,數據的物理存儲結構就有連續存儲和離散存儲兩種,它們對應了我們通常所說的數組和鏈表。

由於數組是連續存儲的,在操作數組中的數據時就可以根據離首地址的偏移量直接存取相應位置上的數據,但是如果要在數據組中任意位置上插入一個元素,就需要先把後面的元素集體向後移一位爲其空出存儲空間。與之相反,鏈表是離散存儲的,所以在插入一個數據時只要申請一片新空間,然後將其中的連接關係做一個修改就可以,但是顯然在鏈表上查找一個數據時就要逐個遍歷了。

考慮以上的總結可見,數組和鏈表各有優缺點。在具體使用時要根據具體情況選擇。當查找數據操作比較多時最好用數組;當對數據集中的數據進行添加或刪除比較多時最好選擇鏈表。

5.2 Kotlin 集合類簡介

集合類存放的都是對象的引用,而非對象本身,我們通常說的集合中的對象指的是集合中對象的引用(reference)。

Kotlin的集合類分爲:可變集合類(Mutable)與不可變集合類(Immutable)。

集合類型主要有3種:list(列表)、set(集)、和 map(映射)。

(1)列表

列表的主要特徵是其對象以線性方式存儲,沒有特定順序,只有一個開頭和一個結尾,當然,它與根本沒有順序的集是不同的。

列表在數據結構中可表現爲:數組和向量、鏈表、堆棧、隊列等。

(2)集

集(set)是最簡單的一種集合,它的對象不按特定方式排序,只是簡單的把對象加入集合中,就像往口袋裏放東西。

對集中成員的訪問和操作是通過集中對象的引用進行的,所以集中不能有重複對象。

集也有多種變體,可以實現排序等功能,如TreeSet,它把對象添加到集中的操作將變爲按照某種比較規則將其插入到有序的對象序列中。它實現的是SortedSet接口,也就是加入了對象比較的方法。通過對集中的對象迭代,我們可以得到一個升序的對象集合。

(3)映射

映射與集或列表有明顯區別,映射中每個項都是成對的。映射中存儲的每個對象都有一個相關的關鍵字(Key)對象,關鍵字決定了 對象在映射中的存儲位置,檢索對象時必須提供相應的關鍵字,就像在字典中查單詞一樣。關鍵字應該是唯一的。

關鍵字本身並不能決定對象的存儲位置,它需要對過一種散列(hashing)技術來處理,產生一個被稱作散列碼(hash code)的整數值,

散列碼通常用作一個偏置量,該偏置量是相對於分配給映射的內存區域起始位置的,由此確定關鍵字/對象對的存儲位置。理想情況 下,散列處理應該產生給定範圍內均勻分佈的值,而且每個關鍵字應得到不同的散列碼。

5.3 List

List接口繼承於Collection接口,元素以線性方式存儲,集合中可以存放重複對象。Kotlin的List分爲:不可變集合類List(ReadOnly, Immutable)和可變集合類MutableList(Read&Write, Mutable)。

其類圖結構如下:

img

其中,Iterator是所有容器類Collection的迭代器。迭代器(Iterator)模式,又叫做遊標(Cursor)模式。GOF給出的定義爲:提供一種方法訪問一個容器對象中各個元素,而又不需暴露該對象的內部細節。 從定義可見,迭代器模式是爲容器而生。

5.3.1 創建不可變List

我們可以使用listOf函數來構建一個不可變的List(read-only,只讀的List)。它定義在libraries/stdlib/src/kotlin/collections/Collections.kt 裏面。關於listOf這個構建函數有下面3個重載函數:

@kotlin.internal.InlineOnly
public inline fun <T> listOf(): List<T> = emptyList()

public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()

@JvmVersion
public fun <T> listOf(element: T): List<T> = java.util.Collections.singletonList(element)

這些函數創建的List都是是隻讀的(readonly,也就是不可變的immutable )、可序列化的。

其中,

  • listOf()用於創建沒有元素的空List
  • listOf(vararg elements: T)用於創建只有一個元素的List
  • listOf(element: T)用於創建擁有多個元素的List

我們使用代碼示例分別來演示其用法:

首先,我們使用listOf()來構建一個沒有元素的空的List:

>>> val list:List<Int> = listOf()
>>> list
[]
>>> list::class
class kotlin.collections.EmptyList

注意,這裏的變量的類型不能省略,否則會報錯:

>>> val list = listOf()
error: type inference failed: Not enough information to infer parameter T in inline fun <T> listOf(): List<T>
Please specify it explicitly.

val list = listOf()
           ^

因爲這是一個泛型函數。關於泛型,我們將在下一章中介紹。

其中,EmptyList 是一個 internal object EmptyList, 這是Kotlin內部定義的一個默認空的object List類。

下面,我們再來創建一個只有1個元素的List:

>>> val list = listOf(1)
>>> list::class
class java.util.Collections$SingletonList

我們可以看出,它實際上是調用Java的java.util.Collections 裏面的singletonList方法:

public static <T> List<T> singletonList(T o) {
    return new SingletonList<>(o);
}

我們再來創建一個有多個元素的List:

>>> val list = listOf(0,1, 2, 3, 4, 5, 6,7,8,9)
>>> list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list::class
class java.util.Arrays$ArrayList
>>> list::class.java

它調用的是

fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()

這個函數。其中,asList函數是Array的擴展函數:

public fun <T> Array<out T>.asList(): List<T> {
    return ArraysUtilJVM.asList(this)
}

而這個ArraysUtilJVM是一個Java類,裏面實際上調用的是java.util.Arraysjava.util.List :

package kotlin.collections;

import java.util.Arrays;
import java.util.List;

class ArraysUtilJVM {
    static <T> List<T> asList(T[] array) {
        return Arrays.asList(array);
    }
}

另外,我們還可以直接使用arrayListOf函數來創建一個Java中的ArrayList對象實例:

>>> val list = arrayListOf(0,1,2,3)
>>> list
[0, 1, 2, 3]
>>> list::class
class java.util.ArrayList
>>> val list = listOf(0,1, 2, 3, 4, 5, 6,7,8,9) 
>>> list::class
class java.util.Arrays$ArrayList

這個函數定義在libraries/stdlib/src/kotlin/collections/Collections.kt類中:

@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> arrayListOf(): ArrayList<T> = ArrayList()

同樣的處理方式,這裏的ArrayList()是Java中的java.util.ArrayList的類型別名:

@SinceKotlin("1.1") public typealias ArrayList<E> = java.util.ArrayList<E>

5.3.2 創建可變集合MutableList

在MutableList中,除了繼承List中的那些函數外,另外新增了add/addAll、remove/removeAll/removeAt、set、clear、retainAll等更新修改的操作函數。

override fun add(element: E): Boolean
override fun remove(element: E): Boolean
override fun addAll(elements: Collection<E>): Boolean
fun addAll(index: Int, elements: Collection<E>): Boolean
override fun removeAll(elements: Collection<E>): Boolean
override fun retainAll(elements: Collection<E>): Boolean
override fun clear(): Unit
operator fun set(index: Int, element: E): E
fun add(index: Int, element: E): Unit
fun removeAt(index: Int): E
override fun listIterator(): MutableListIterator<E>
override fun listIterator(index: Int): MutableListIterator<E>
override fun subList(fromIndex: Int, toIndex: Int): MutableList<E>

創建一個MutableList的對象實例跟List類似,前面加上前綴mutable,代碼示例如下:

>>> val list = mutableListOf(1, 2, 3)
>>> list
[1, 2, 3]
>>> list::class
class java.util.ArrayList
>>> val list2 = mutableListOf<Int>()
>>> list2
[]
>>> list2::class
class java.util.ArrayList
>>> val list3 = mutableListOf(1)
>>> list3
[1]
>>> list3::class
class java.util.ArrayList

我們可以看出,使用mutableListOf函數創建的可變集合類,實際上背後調用的是java.util.ArrayList類的相關方法。

另外,我們可以直接使用Kotlin封裝的arrayListOf函數來創建一個ArrayList:

>>> val list4 = arrayListOf(1, 2, 3)
>>> list4::class
class java.util.ArrayList

關於Kotlin中的ArrayList類型別名定義在
kotlin/collections/TypeAliases.kt 文件中:

@file:kotlin.jvm.JvmVersion

package kotlin.collections

@SinceKotlin("1.1") public typealias RandomAccess = java.util.RandomAccess


@SinceKotlin("1.1") public typealias ArrayList<E> = java.util.ArrayList<E>
@SinceKotlin("1.1") public typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
@SinceKotlin("1.1") public typealias HashMap<K, V> = java.util.HashMap<K, V>
@SinceKotlin("1.1") public typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
@SinceKotlin("1.1") public typealias HashSet<E> = java.util.HashSet<E>


// also @SinceKotlin("1.1")
internal typealias SortedSet<E> = java.util.SortedSet<E>
internal typealias TreeSet<E> = java.util.TreeSet<E>

如果我們已經有了一個不可變的List,而我們現在想把他轉換成可變的List,我們可以直接調用轉換函數toMutableList

val list = mutableListOf(1, 2, 3)
val mlist = list.toMutableList()
mlist.add(5)

5.3.3 遍歷List元素

使用Iterator迭代器

我們以集合 val list = listOf(0,1, 2, 3, 4, 5, 6,7,8,9)爲例,使用Iterator迭代器遍歷列表所有元素的操作:

>>> val list = listOf(0,1, 2, 3, 4, 5, 6,7,8,9) 
>>> list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> val iterator = list.iterator()
>>> iterator
java.util.AbstractList$Itr@438bad7c
>>> while(iterator.hasNext()){
... println(iterator.next())
... }
0
1
2
3
4
5
6
7
8
9

迭代器是一種設計模式,它是一個對象,它可以遍歷並選擇序列中的對象,而開發人員不需要了解該序列的底層結構。迭代器通常被稱爲“輕量級”對象,因爲創建它的代價小。

Kotlin中的Iterator功能比較簡單,並且只能單向移動:

(1)調用iterator()函數,容器返回一個Iterator實例。iterator()函數是kotlin.collections.Iterable中的函數, 被Collection繼承。
(2)調用hasNext()函數檢查序列中是否還有元素。
(3)第一次調用Iterator的next()函數時,它返回序列的第一個元素。依次向後遞推,使用next()獲得序列中的下一個元素。

當我們調用到最後一個元素,再次調用next()函數,會拋這個異常java.util.NoSuchElementException。代碼示例:

>>> val list = listOf(1,2,3)
>>> val iter = list.iterator()
>>> iter
java.util.AbstractList$Itr@3abfe845
>>> iter.hasNext()
true
>>> iter.next()
1
>>> iter.hasNext()
true
>>> iter.next()
2
>>> iter.hasNext()
true
>>> iter.next()
3
>>> iter.hasNext()
false
>>> iter.next()
java.util.NoSuchElementException
    at java.util.AbstractList$Itr.next(AbstractList.java:364)

我們可以看出,這裏的Iterator的實現是在AbstractList中的內部類IteratorImpl

private open inner class IteratorImpl : Iterator<E> {
    protected var index = 0
    override fun hasNext(): Boolean = index < size
    override fun next(): E {
        if (!hasNext()) throw NoSuchElementException()
        return get(index++)
    }
}

通過這個實現源碼,我們可以更加清楚地明白Iterator的工作原理。

其中,NoSuchElementException()這個類是java.util.NoSuchElementException的類型別名:

@kotlin.SinceKotlin public typealias NoSuchElementException = java.util.NoSuchElementException

使用forEach遍歷List元素

這個forEach函數定義如下:

@kotlin.internal.HidesMembers
public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}

它是package kotlin.collections包下面的Iterable的擴展內聯函數。它的入參是一個函數類型:

action: (T) -> Unit

關於函數式編程,我們將在後面章節中學習。

這裏的forEach是一個語法糖。實際上forEach在遍歷List對象的時候,仍然使用的是iterator迭代器來進行循環遍歷的。

>>> val list = listOf(1,2,3)
>>> list
[1, 2, 3]
>>> list.forEach{
... println(it)
... }
1
2
3

當參數只有一個函數的時候,括號可以省略不寫。

也就是說,這裏面的forEach函數調用的寫法,實際上跟下面的寫法等價:

list.forEach({
    println(it)
})

我們甚至還可以直接這樣寫:

>>> list.forEach(::println)

其中,:: 是函數引用符。

5.3.4 List元素操作函數

add remove set clear

這兩個添加、刪除操作函數是MutableList裏面的。跟Java中的集合類操作類似。

創建一個可變集合:

>>> val mutableList = mutableListOf(1,2,3)
>>> mutableList
[1, 2, 3]

向集合中添加一個元素:

>>> mutableList.add(4)
true
>>> mutableList
[1, 2, 3, 4]

在下標爲0的位置添加元素0 :

>>> mutableList.add(0,0)
>>> mutableList
[0, 1, 2, 3, 4]

刪除元素1 :

>>> mutableList.remove(1)
true
>>> mutableList
[0, 2, 3, 4]
>>> mutableList.remove(1)
false

刪除下標爲1的元素:

>>> mutableList.removeAt(1)
2
>>> mutableList
[0, 3, 4]

刪除子集合:

>>> mutableList.removeAll(listOf(3,4))
true
>>> mutableList
[0]

添加子集合:

>>> mutableList.addAll(listOf(1,2,3))
true
>>> mutableList
[1, 2, 3]

更新設置下標0的元素值爲100:

>>> mutableList.set(0,100)
0
>>> mutableList
[100]

清空集合:

>>> mutableList.clear()
>>> mutableList
[]

把可變集合轉爲不可變集合:

>>> mutableList.toList()
[1, 2, 3]

retainAll

取兩個集合交集:

>>> val mlist1 = mutableListOf(1,2,3,4,5,6)
>>> val mlist2 = mutableListOf(3,4,5,6,7,8,9)
>>> mlist1.retainAll(mlist2)
true
>>> mlist1
[3, 4, 5, 6]

contains(element: T): Boolean

判斷集合中是否有指定元素,有就返回true,否則返回false 。
代碼示例:

>>> val list = listOf(1,2,3,4,5,6,7)
>>> list.contains(1)
true

elementAt(index: Int): T

查找下標對應的元素,如果下標越界會拋IndexOutOfBoundsException。
代碼示例:

>>> val list = listOf(1,2,3,4,5,6,7)
>>> list.elementAt(6)
7
>>> list.elementAt(7)
java.lang.ArrayIndexOutOfBoundsException: 7
    at java.util.Arrays$ArrayList.get(Arrays.java:3841)

另外,針對越界的處理,還有下面兩個函數:

elementAtOrElse(index: Int, defaultValue: (Int) -> T): T : 查找下標對應元素,如果越界會根據方法返回默認值。

>>> list.elementAtOrElse(7,{0})
0
>>> list.elementAtOrElse(7,{10})
10

elementAtOrNull(index: Int): T? : 查找下標對應元素,如果越界就返回null

>>> list.elementAtOrNull(7)
null

first()

返回集合第1個元素,如果是空集,拋出異常NoSuchElementException。

>>> val list = listOf(1,2,3)
>>> list.first()
1
>>> val emptyList = listOf<Int>()
>>> emptyList.first()
java.util.NoSuchElementException: List is empty.
    at kotlin.collections.CollectionsKt___CollectionsKt.first(_Collections.kt:178)

對應的有針對異常處理的函數firstOrNull(): T? :

>>> emptyList.firstOrNull()
null

first(predicate: (T) -> Boolean): T

返回符合條件的第一個元素,沒有則拋異常NoSuchElementException 。

>>> val list = listOf(1,2,3)
>>> list.first({it%2==0})
2
>>> list.first({it>100})
java.util.NoSuchElementException: Collection contains no element matching the predicate.

對應的有針對異常處理的函數firstOrNull(predicate: (T) -> Boolean): T? ,返回符合條件的第一個元素,沒有就返回null :

>>> list.firstOrNull({it>100})
null

indexOf(element: T): Int

返回指定下標的元素,沒有就返回-1

>>> val list = listOf("a","b","c")
>>> list.indexOf("c")
2
>>> list.indexOf("x")
-1

indexOfFirst(predicate: (T) -> Boolean): Int

返回第一個符合條件的元素下標,沒有就返回-1 。

>>> val list = listOf("abc","xyz","xjk","pqk")
>>> list.indexOfFirst({it.contains("x")})
1
>>> list.indexOfFirst({it.contains("k")})
2
>>> list.indexOfFirst({it.contains("e")})
-1

indexOfLast(predicate: (T) -> Boolean): Int

返回最後一個符合條件的元素下標,沒有就返回-1 。

>>> val list = listOf("abc","xyz","xjk","pqk")
>>> list.indexOfLast({it.contains("x")})
2
>>> list.indexOfLast({it.contains("k")})
3
>>> list.indexOfLast({it.contains("e")})
-1

last()

返回集合最後一個元素,空集則拋出異常NoSuchElementException。

>>> val list = listOf(1,2,3,4,7,5,6,7,8)
>>> list.last()
8
>>> val emptyList = listOf<Int>()
>>> emptyList.last()
java.util.NoSuchElementException: List is empty.
    at kotlin.collections.CollectionsKt___CollectionsKt.last(_Collections.kt:340)

last(predicate: (T) -> Boolean): T

返回符合條件的最後一個元素,沒有就拋NoSuchElementException

>>> val list = listOf(1,2,3,4,7,5,6,7,8)
>>> list.last({it==7})
7
>>> list.last({it>10})
java.util.NoSuchElementException: List contains no element matching the predicate.

對應的針對越界處理的lastOrNull函數:返回符合條件的最後一個元素,沒有則返回null :

>>> list.lastOrNull({it>10})
null

lastIndexOf(element: T): Int

返回符合條件的最後一個元素,沒有就返回-1

>>> val list = listOf("abc","dfg","jkl","abc","bbc","wer")
>>> list.lastIndexOf("abc")
3

single(): T

該集合如果只有1個元素,則返回該元素。否則,拋異常。

>>> val list = listOf(1)
>>> list.single()
1

>>> val list = listOf(1,2)
>>> list.single()
java.lang.IllegalArgumentException: List has more than one element.
    at kotlin.collections.CollectionsKt___CollectionsKt.single(_Collections.kt:471)

>>> val list = listOf<Int>()

>>> list.single()
java.util.NoSuchElementException: List is empty.
    at kotlin.collections.CollectionsKt___CollectionsKt.single(_Collections.kt:469)

single(predicate: (T) -> Boolean): T

返回符合條件的單個元素,如有沒有符合的拋異常NoSuchElementException,或超過一個的拋異常IllegalArgumentException。

>>> val list = listOf(1,2,3,4,7,5,6,7,8)
>>> list.single({it==1})
1
>>> list.single({it==7})
java.lang.IllegalArgumentException: Collection contains more than one matching element.

>>> list.single({it==10})
java.util.NoSuchElementException: Collection contains no element matching the predicate.

對應的針對異常處理的函數singleOrNull: 返回符合條件的單個元素,如有沒有符合或超過一個,返回null

>>> list.singleOrNull({it==7})
null
>>> list.singleOrNull({it==10})
null

5.3.5 List集合類的any all ``none ``count ``reduce ``fold ``max ``min sum 函數算子(operator)

any()判斷集合至少有一個元素

這個函數定義如下:

public fun <T> Iterable<T>.any(): Boolean {
    for (element in this) return true
    return false
}

如果該集合至少有一個元素,返回true,否則返回false

代碼示例:

>>> val emptyList = listOf<Int>()
>>> emptyList.any()
false
>>> val list1 = listOf(1)
>>> list1.any()
true

any(predicate: (T) -> Boolean) 判斷集合中是否有滿足條件的元素

這個函數定義如下:

public inline fun <T> Iterable<T>.any(predicate: (T) -> Boolean): Boolean {
    for (element in this) if (predicate(element)) return true
    return false
}

如果該集合中至少有一個元素匹配謂詞函數參數predicate: (T) -> Boolean,返回true,否則返回false。

代碼示例:

>>> val list = listOf(1, 2, 3)
>>> list.any() // 至少有1個元素
true
>>> list.any({it%2==0}) // 元素2滿足{it%2==0}
true
>>> list.any({it>4}) // 沒有元素滿足{it>4}
false

all(predicate: (T) -> Boolean) 判斷集合中的元素是否都滿足條件

函數定義:

public inline fun <T> Iterable<T>.all(predicate: (T) -> Boolean): Boolean {
    for (element in this) if (!predicate(element)) return false
    return true
}

當且僅當該集合中所有元素都滿足條件時,返回true;否則都返回false

代碼示例:

>>> val list = listOf(0,2,4,6,8)
>>> list.all({it%2==0})
true
>>> list.all({it>2})
false

none()判斷集合無元素

函數定義:

public fun <T> Iterable<T>.none(): Boolean {
    for (element in this) return false
    return true
}

如果該集合沒有任何元素,返回true,否則返回false

代碼示例:

>>> val list = listOf<Int>()
>>> list.none()
true

none(predicate: (T) -> Boolean)判斷集合中所有元素都不滿足條件

函數定義:

public inline fun <T> Iterable<T>.none(predicate: (T) -> Boolean): Boolean {
    for (element in this) if (predicate(element)) return false
    return true
}

當且僅當集合中所有元素都不滿足條件時返回true,否則返回false

代碼示例:

>>> val list = listOf(0,2,4,6,8)
>>> list.none({it%2==1})
true
>>> list.none({it>0})
false

count() 計算集合中元素的個數

函數定義:

public fun <T> Iterable<T>.count(): Int {
    var count = 0
    for (element in this) count++
    return count
}

代碼示例:

>>> val list = listOf(0,2,4,6,8,9)
>>> list.count()
6

count(predicate: (T) -> Boolean) 計算集合中滿足條件的元素的個數

函數定義:

public inline fun <T> Iterable<T>.count(predicate: (T) -> Boolean): Int {
    var count = 0
    for (element in this) if (predicate(element)) count++
    return count
}

代碼示例:

>>> val list = listOf(0,2,4,6,8,9)
>>> list.count()
6
>>> list.count({it%2==0})
5

reduce從 第一項到最後一項進行累計運算

函數定義:

public inline fun <S, T: S> Iterable<T>.reduce(operation: (acc: S, T) -> S): S {
    val iterator = this.iterator()
    if (!iterator.hasNext()) throw UnsupportedOperationException("Empty collection can't be reduced.")
    var accumulator: S = iterator.next()
    while (iterator.hasNext()) {
        accumulator = operation(accumulator, iterator.next())
    }
    return accumulator
}

首先把第一個元素賦值給累加子accumulator,然後逐次向後取元素累加,新值繼續賦值給累加子accumulator = operation(accumulator, iterator.next()),以此類推。最後返回累加子的值。

代碼示例:

>>> val list = listOf(1,2,3,4,5,6,7,8,9)
>>> list.reduce({sum, next->sum+next})
45
>>> list.reduce({sum, next->sum*next})
362880
>>> val list = listOf("a","b","c")
>>> list.reduce({total, s->total+s})
abc

reduceRight從最後一項到第一項進行累計運算

函數定義:

public inline fun <S, T: S> List<T>.reduceRight(operation: (T, acc: S) -> S): S {
    val iterator = listIterator(size)
    if (!iterator.hasPrevious())
        throw UnsupportedOperationException("Empty list can't be reduced.")
    var accumulator: S = iterator.previous()
    while (iterator.hasPrevious()) {
        accumulator = operation(iterator.previous(), accumulator)
    }
    return accumulator
}

從函數的定義accumulator = operation(iterator.previous(), accumulator), 我們可以看出,從右邊累計運算的累加子是放在後面的。

代碼示例:

>>> val list = listOf("a","b","c")
>>> list.reduceRight({total, s -> s+total})
cba

如果我們位置放錯了,會輸出下面的結果:

>>> list.reduceRight({total, s -> total+s})
abc

fold(initial: R, operation: (acc: R, T) -> R): R 帶初始值的reduce

函數定義:

public inline fun <T, R> Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
    var accumulator = initial
    for (element in this) accumulator = operation(accumulator, element)
    return accumulator
}

從函數的定義,我們可以看出,fold函數給累加子賦了初始值initial

代碼示例:

>>> val list=listOf(1,2,3,4)
>>> list.fold(100,{total, next -> next + total})
110

foldRightreduceRight類似,有初始值。

函數定義:

public inline fun <T, R> List<T>.foldRight(initial: R, operation: (T, acc: R) -> R): R {
    var accumulator = initial
    if (!isEmpty()) {
        val iterator = listIterator(size)
        while (iterator.hasPrevious()) {
            accumulator = operation(iterator.previous(), accumulator)
        }
    }
    return accumulator
}

代碼示例:

>>> val list = listOf("a","b","c")
>>> list.foldRight("xyz",{s, pre -> pre + s})
xyzcba

forEach(action: (T) -> Unit): Unit 循環遍歷元素,元素是it

我們在前文已經講述,參看5.3.4。

再寫個代碼示例:

>>> val list = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 
>>> list.forEach { value -> if (value > 7) println(value) } 
8
9

forEachIndexed 帶index(下標) 的元素遍歷

函數定義:

public inline fun <T> Iterable<T>.forEachIndexed(action: (index: Int, T) -> Unit): Unit {
    var index = 0
    for (item in this) action(index++, item)
}

代碼示例:

>>> val list = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 
>>> list.forEachIndexed { index, value -> if (value > 8) println("value of index $index is $value, greater than 8") } 

value of index 9 is 9, greater than 8

maxmin查詢最大、最小的元素,空集則返回null

max函數定義:

public fun <T : Comparable<T>> Iterable<T>.max(): T? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var max = iterator.next()
    while (iterator.hasNext()) {
        val e = iterator.next()
        if (max < e) max = e
    }
    return max
}

返回集合中最大的元素。

代碼示例:

>>> val list = listOf(1,2,3)
>>> list.max()
3
>>> val list = listOf("a","b","c")
>>> list.max()
c

min函數定義:

public fun <T : Comparable<T>> Iterable<T>.min(): T? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var min = iterator.next()
    while (iterator.hasNext()) {
        val e = iterator.next()
        if (min > e) min = e
    }
    return min
}

返回集合中的最小元素。

代碼示例:

>>> val list = listOf(1,2,3)
>>> list.min()
1
>>> val list = listOf("a","b","c")
>>> list.min()
a

在Kotlin中,字符串的大小比較比較有意思的,我們直接通過代碼示例來學習一下:

>>> "c" > "a"
true
>>> "abd" > "abc"
true
>>> "abd" > "abcd"
true
>>> "abd" > "abcdefg"
true

我們可以看出,字符串的大小比較是按照對應的下標的字符進行比較的。
另外,布爾值的比較是true大於false

>>> true > false
true

maxBy(selector: (T) -> R): T?minBy(selector: (T) -> R): T?獲取函數映射結果的最大值、最小值對應的那個元素的值,如果沒有則返回null

函數定義:

public inline fun <T, R : Comparable<R>> Iterable<T>.maxBy(selector: (T) -> R): T? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var maxElem = iterator.next()
    var maxValue = selector(maxElem)
    while (iterator.hasNext()) {
        val e = iterator.next()
        val v = selector(e)
        if (maxValue < v) {
            maxElem = e
            maxValue = v
        }
    }
    return maxElem
}

也就是說,不直接比較集合元素的大小,而是以集合元素爲入參的函數selector: (T) -> R 返回值來比較大小,最後返回此元素的值(注意,不是對應的selector函數的返回值)。有點像數學裏的求函數最值問題:

給定函數 y = f(x) , 求max f(x)x的值。

代碼示例:

>>> val list = listOf(100,-500,300,200)
>>> list.maxBy({it})
300
>>> list.maxBy({it*(1-it)})
100
>>> list.maxBy({it*it})
-500

對應的 minBy 是獲取函數映射後返回結果的最小值所對應那個元素的值,如果沒有則返回null。

代碼示例:

>>> val list = listOf(100,-500,300,200)
>>> list.minBy({it})
-500
>>> list.minBy({it*(1-it)})
-500
>>> list.minBy({it*it})
100

sumBy(selector: (T) -> Int): Int 獲取函數映射值的總和

函數定義:

public inline fun <T> Iterable<T>.sumBy(selector: (T) -> Int): Int {
    var sum: Int = 0
    for (element in this) {
        sum += selector(element)
    }
    return sum
}

可以看出,這個sumBy函數算子,累加器sum初始值爲0,返回值是Int。它的入參selector是一個函數類型(T) -> Int,也就是說這個selector也是返回Int類型的函數。

代碼示例:

>>> val list = listOf(1,2,3,4)
>>> list.sumBy({it})
10
>>> list.sumBy({it*it})
30

類型錯誤反例:

>>> val list = listOf("a","b","c")
>>> list.sumBy({it})
error: type inference failed: inline fun <T> Iterable<T>.sumBy(selector: (T) -> Int): Int
cannot be applied to
receiver: List<String>  arguments: ((String) -> String)

list.sumBy({it})
     ^
error: type mismatch: inferred type is (String) -> String but (String) -> Int was expected
list.sumBy({it})
           ^

5.3.6 過濾操作函數算子

take(n: Int): List<T> 挑出該集合前n個元素的子集合

函數定義:

public fun <T> Iterable<T>.take(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return emptyList()
    if (this is Collection<T>) {
        if (n >= size) return toList()
        if (n == 1) return listOf(first())
    }
    var count = 0
    val list = ArrayList<T>(n)
    for (item in this) {
        if (count++ == n)
            break
        list.add(item)
    }
    return list.optimizeReadOnlyList()
}

如果n等於0,返回空集;如果n大於集合size,返回該集合。

代碼示例:

>>> val list = listOf("a","b","c")
>>> list
[a, b, c]
>>> list.take(2)
[a, b]
>>> list.take(10)
[a, b, c]
>>> list.take(0)
[]

takeWhile(predicate: (T) -> Boolean): List<T> 挑出滿足條件的元素的子集合

函數定義:

public inline fun <T> Iterable<T>.takeWhile(predicate: (T) -> Boolean): List<T> {
    val list = ArrayList<T>()
    for (item in this) {
        if (!predicate(item))
            break
        list.add(item)
    }
    return list
}

從第一個元素開始,判斷是否滿足predicate爲true,如果滿足條件的元素就丟到返回ArrayList中。只要遇到任何一個元素不滿足條件,就結束循環,返回list 。

代碼示例:

>>> val list = listOf(1,2,4,6,8,9)
>>> list.takeWhile({it%2==0})
[]
>>> list.takeWhile({it%2==1})
[1]

>>> val list = listOf(2,4,6,8,9,11,12,16)
>>> list.takeWhile({it%2==0})
[2, 4, 6, 8]

takeLast 挑出後n個元素的子集合

函數定義:

public fun <T> List<T>.takeLast(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return emptyList()
    val size = size
    if (n >= size) return toList()
    if (n == 1) return listOf(last())
    val list = ArrayList<T>(n)
    if (this is RandomAccess) {
        for (index in size - n .. size - 1)
            list.add(this[index])
    } else {
        for (item in listIterator(n))
            list.add(item)
    }
    return list
}

從集合倒數n個元素起,取出到最後一個元素的子集合。如果傳入0,返回空集。如果傳入n大於集合size,返回整個集合。如果傳入負數,直接拋出IllegalArgumentException。

代碼示例:

>>> val list = listOf(2,4,6,8,9,11,12,16)
>>> list.takeLast(0)
[]
>>> list.takeLast(3)
[11, 12, 16]
>>> list.takeLast(100)
[2, 4, 6, 8, 9, 11, 12, 16]
>>> list.takeLast(-1)
java.lang.IllegalArgumentException: Requested element count -1 is less than zero.
    at kotlin.collections.CollectionsKt___CollectionsKt.takeLast(_Collections.kt:734)

takeLastWhile(predicate: (T) -> Boolean) 從最後開始挑出滿足條件元素的子集合

函數定義:

public inline fun <T> List<T>.takeLastWhile(predicate: (T) -> Boolean): List<T> {
    if (isEmpty())
        return emptyList()
    val iterator = listIterator(size)
    while (iterator.hasPrevious()) {
        if (!predicate(iterator.previous())) {
            iterator.next()
            val expectedSize = size - iterator.nextIndex()
            if (expectedSize == 0) return emptyList()
            return ArrayList<T>(expectedSize).apply {
                while (iterator.hasNext())
                    add(iterator.next())
            }
        }
    }
    return toList()
}

反方向取滿足條件的元素,遇到不滿足的元素,直接終止循環,並返回子集合。

代碼示例:

>>> val list = listOf(2,4,6,8,9,11,12,16)
>>> list.takeLastWhile({it%2==0})
[12, 16]

drop(n: Int) 去除前n個元素返回剩下的元素的子集合

函數定義:

public fun <T> Iterable<T>.drop(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return toList()
    val list: ArrayList<T>
    if (this is Collection<*>) {
        val resultSize = size - n
        if (resultSize <= 0)
            return emptyList()
        if (resultSize == 1)
            return listOf(last())
        list = ArrayList<T>(resultSize)
        if (this is List<T>) {
            if (this is RandomAccess) {
                for (index in n..size - 1)
                    list.add(this[index])
            } else {
                for (item in listIterator(n))
                    list.add(item)
            }
            return list
        }
    }
    else {
        list = ArrayList<T>()
    }
    var count = 0
    for (item in this) {
        if (count++ >= n) list.add(item)
    }
    return list.optimizeReadOnlyList()
}

代碼示例:

>>> val list = listOf(2,4,6,8,9,11,12,16)
>>> list.drop(5)
[11, 12, 16]
>>> list.drop(100)
[]
>>> list.drop(0)
[2, 4, 6, 8, 9, 11, 12, 16]
>>> list.drop(-1)
java.lang.IllegalArgumentException: Requested element count -1 is less than zero.
    at kotlin.collections.CollectionsKt___CollectionsKt.drop(_Collections.kt:538)

dropWhile(predicate: (T) -> Boolean) 去除滿足條件的元素返回剩下的元素的子集合

函數定義:

public inline fun <T> Iterable<T>.dropWhile(predicate: (T) -> Boolean): List<T> {
    var yielding = false
    val list = ArrayList<T>()
    for (item in this)
        if (yielding)
            list.add(item)
        else if (!predicate(item)) {
            list.add(item)
            yielding = true
        }
    return list
}

去除滿足條件的元素,當遇到一個不滿足條件的元素時,中止操作,返回剩下的元素子集合。

代碼示例:

>>> val list = listOf(2,4,6,8,9,11,12,16)
>>> list.dropWhile({it%2==0})
[9, 11, 12, 16]

dropLast(n: Int) 從最後去除n個元素

函數定義:

public fun <T> List<T>.dropLast(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    return take((size - n).coerceAtLeast(0))
}

代碼示例:

>>> val list = listOf(2,4,6,8,9,11,12,16)
>>> list.dropLast(3)
[2, 4, 6, 8, 9]
>>> list.dropLast(100)
[]
>>> list.dropLast(0)
[2, 4, 6, 8, 9, 11, 12, 16]
>>> list.dropLast(-1)
java.lang.IllegalArgumentException: Requested element count -1 is less than zero.
    at kotlin.collections.CollectionsKt___CollectionsKt.dropLast(_Collections.kt:573)

dropLastWhile(predicate: (T) -> Boolean) 從最後滿足條件的元素

函數定義:

public inline fun <T> List<T>.dropLastWhile(predicate: (T) -> Boolean): List<T> {
    if (!isEmpty()) {
        val iterator = listIterator(size)
        while (iterator.hasPrevious()) {
            if (!predicate(iterator.previous())) {
                return take(iterator.nextIndex() + 1)
            }
        }
    }
    return emptyList()
}

代碼示例:

>>> val list = listOf(2,4,6,8,9,11,12,16)
>>> list.dropLastWhile({it%2==0})
[2, 4, 6, 8, 9, 11]

slice(indices: IntRange) 取開始下標至結束下標元素子集合

函數定義:

public fun <T> List<T>.slice(indices: IntRange): List<T> {
    if (indices.isEmpty()) return listOf()
    return this.subList(indices.start, indices.endInclusive + 1).toList()
}

代碼示例:

val list = listOf(2,4,6,8,9,11,12,16)
>>> list
[2, 4, 6, 8, 9, 11, 12, 16]
>>> list.slice(1..3)
[4, 6, 8]
>>> list.slice(2..7)
[6, 8, 9, 11, 12, 16]
>>> list
[2, 4, 6, 8, 9, 11, 12, 16]
>>> list.slice(1..3)
[4, 6, 8]
>>> list.slice(2..7)
[6, 8, 9, 11, 12, 16]

slice(indices: Iterable<Int>)返回指定下標的元素子集合

函數定義:

public fun <T> List<T>.slice(indices: Iterable<Int>): List<T> {
    val size = indices.collectionSizeOrDefault(10)
    if (size == 0) return emptyList()
    val list = ArrayList<T>(size)
    for (index in indices) {
        list.add(get(index))
    }
    return list
}

這個函數從簽名上看,不是那麼簡單直接。從函數的定義看,這裏的indices是當做原來集合的下標來使用的。

代碼示例:

>>> list
[2, 4, 6, 8, 9, 11, 12, 16]
>>> list.slice(listOf(2,4,6))
[6, 9, 12]

我們可以看出,這裏是取出下標爲2,4,6的元素。而不是直觀理解上的,去掉元素2,4,6。

filterTo(destination: C, predicate: (T) -> Boolean) 過濾出滿足條件的元素並賦值給destination

函數定義:

public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C {
    for (element in this) if (predicate(element)) destination.add(element)
    return destination
}

把滿足過濾條件的元素組成的子集合賦值給入參destination。

代碼示例:

>>> val list = listOf(1,2,3,4,5,6,7)
>>> val dest = mutableListOf<Int>()
>>> list.filterTo(dest,{it>3})
[4, 5, 6, 7]
>>> dest
[4, 5, 6, 7]

filter(predicate: (T) -> Boolean)過濾出滿足條件的元素組成的子集合

函數定義:

public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}

相對於filterTo函數,filter函數更加簡單易用。從源碼我們可以看出,filter函數直接調用的filterTo(ArrayList<T>(), predicate), 其中入參destination被直接默認賦值爲ArrayList<T>()

代碼示例:

>>> val list = listOf(1,2,3,4,5,6,7)
>>> list.filter({it>3})
[4, 5, 6, 7]

另外,還有下面常用的過濾函數:

filterNot(predicate: (T) -> Boolean), 用來過濾所有不滿足條件的元素 ;
filterNotNull() 過濾掉null元素。

5.3.7 映射操作符

map(transform: (T) -> R): List<R>

將集合中的元素通過轉換函數transform映射後的結果,存到一個集合中返回。

>>> val list = listOf(1,2,3,4,5,6,7)
>>> list.map({it})
[1, 2, 3, 4, 5, 6, 7]
>>> list.map({it*it})
[1, 4, 9, 16, 25, 36, 49]
>>> list.map({it+10})
[11, 12, 13, 14, 15, 16, 17]

這個函數內部調用的是

public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
    return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}

這裏的mapTo函數定義如下:

public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C {
    for (item in this)
        destination.add(transform(item))
    return destination
}

我們可以看出,這個map實現的原理是循環遍歷原集合中的元素,並把通過transform映射後的結果放到一個新的destination集合中,並返回destination。

mapIndexed(transform: (kotlin.Int, T) -> R)

轉換函數transform中帶有下標參數。也就是說我們可以同時使用下標和元素的值來進行轉換。 其中,第一個參數是Int類型的下標。

代碼示例:

>>> val list = listOf(1,2,3,4,5,6,7)
>>> list.mapIndexed({index,it -> index*it})
[0, 2, 6, 12, 20, 30, 42]

mapNotNull(transform: (T) -> R?)

遍歷集合每個元素,得到通過函數算子transform映射之後的值,剔除掉這些值中的null,返回一個無null元素的集合。

代碼示例:

>>> val list = listOf("a","b",null,"x",null,"z")
>>> list.mapNotNull({it})
[a, b, x, z]

這個函數內部實現是調用的mapNotNullTo函數:

public inline fun <T, R : Any, C : MutableCollection<in R>> Iterable<T>.mapNotNullTo(destination: C, transform: (T) -> R?): C {
    forEach { element -> transform(element)?.let { destination.add(it) } }
    return destination
}

flatMap(transform: (T) -> Iterable<R>): List<R>

在原始集合的每個元素上調用transform轉換函數,得到的映射結果組成的單個列表。爲了更簡單的理解這個函數,我們跟map(transform: (T) -> R): List<R>對比下。

首先看函數的各自的實現:

map:

public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
    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)
        destination.add(transform(item))
    return destination
}

flatMap:

public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
    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) {
        val list = transform(element)
        destination.addAll(list)
    }
    return destination
}

我們可以看出,這兩個函數主要區別在transform函數返回上。

代碼示例

>>> val list = listOf("a","b","c")
>>> list.map({it->listOf(it+1,it+2,it+3)})
[[a1, a2, a3], [b1, b2, b3], [c1, c2, c3]]
>>> list.flatMap({it->listOf(it+1,it+2,it+3)})
[a1, a2, a3, b1, b2, b3, c1, c2, c3]

從代碼運行結果我們可以看出,使用 map 是把list中的每一個元素都映射成一個List-n,然後以這些List-n爲元素,組成一個大的嵌套的List返回。而使用flatMap則是把list中的第一個元素映射成一個List1,然後把第二個元素映射成的List2跟List1合併:List1.addAll(List2),以此類推。最終返回一個“扁平的”(flat)List。

其實,這個flatMap的過程是 map + flatten兩個操作的組合。這個flatten函數定義如下:

public fun <T> Iterable<Iterable<T>>.flatten(): List<T> {
    val result = ArrayList<T>()
    for (element in this) {
        result.addAll(element)
    }
    return result
}

代碼示例:

>>> val list = listOf("a","b","c")
>>> list.map({it->listOf(it+1,it+2,it+3)})
[[a1, a2, a3], [b1, b2, b3], [c1, c2, c3]]
>>> list.map({it->listOf(it+1,it+2,it+3)}).flatten()
[a1, a2, a3, b1, b2, b3, c1, c2, c3]

5.3.8 分組操作符

groupBy(keySelector: (T) -> K): Map<K, List<T>>

將集合中的元素按照條件選擇器keySelector(是一個函數)分組,並返回Map。
代碼示例:

>>> val words = listOf("a", "abc", "ab", "def", "abcd")
>>> val lengthGroup = words.groupBy { it.length }
>>> lengthGroup
{1=[a], 3=[abc, def], 2=[ab], 4=[abcd]}

groupBy(keySelector: (T) -> K, valueTransform: (T) -> V)

分組函數還有一個是groupBy(keySelector: (T) -> K, valueTransform: (T) -> V),根據條件選擇器keySelector和轉換函數valueTransform分組。

代碼示例

>>> val programmer = listOf("K&R" to "C", "Bjar" to "C++", "Linus" to "C", "James" to "Java")
>>> programmer
[(K&R, C), (Bjar, C++), (Linus, C), (James, Java)]
>>> programmer.groupBy({it.second}, {it.first})
{C=[K&R, Linus], C++=[Bjar], Java=[James]}

這裏涉及到一個 二元組Pair 類,該類是Kotlin提供的用來處理二元數據組的。 可以理解成Map中的一個鍵值對,比如Pair(“key”,”value”) 等價於 “key” to “value”。

我們再通過下面的代碼示例,來看一下這兩個分組的區別:

>>> val words = listOf("a", "abc", "ab", "def", "abcd")
>>> words.groupBy( { it.length })
{1=[a], 3=[abc, def], 2=[ab], 4=[abcd]}
>>> words.groupBy( { it.length },{it.contains("b")})
{1=[false], 3=[true, false], 2=[true], 4=[true]}

我們可以看出,後者是在前者的基礎上又映射了一次{it.contains("b")},把第2次映射的結果放到返回的Map中了。

groupingBy(crossinline keySelector: (T) -> K): Grouping<T, K>

另外,我們還可以使用groupingBy(crossinline keySelector: (T) -> K): Grouping<T, K>函數來創建一個Grouping,然後調用計數函數eachCount統計分組:

代碼示例

>>> val words = "one two three four five six seven eight nine ten".split(' ')
>>> words.groupingBy({it.first()}).eachCount()
{o=1, t=3, f=2, s=2, e=1, n=1}

上面的例子是統計words列表的元素單詞中首字母出現的頻數。

其中,eachCount函數定義如下:

@SinceKotlin("1.1")
@JvmVersion
public fun <T, K> Grouping<T, K>.eachCount(): Map<K, Int> =
        // fold(0) { acc, e -> acc + 1 } optimized for boxing
        foldTo( destination = mutableMapOf(),
                initialValueSelector = { _, _ -> kotlin.jvm.internal.Ref.IntRef() },
                operation = { _, acc, _ -> acc.apply { element += 1 } })
        .mapValuesInPlace { it.value.element }

5.3.9 排序操作符

reversed(): List<T>

倒序排列集合元素。
代碼示例

>>> val list = listOf(1,2,3)
>>> list.reversed()
[3, 2, 1]

這個函數,Kotlin是直接調用的java.util.Collections.reverse()方法。其相關代碼如下:

public fun <T> Iterable<T>.reversed(): List<T> {
    if (this is Collection && size <= 1) return toList()
    val list = toMutableList()
    list.reverse()
    return list
}

public fun <T> MutableList<T>.reverse(): Unit {
    java.util.Collections.reverse(this)
}

sortedsortedDescending

升序排序和降序排序。

代碼示例

>>> val list = listOf(1,3,2)
>>> list.sorted()
[1, 2, 3]
>>> list.sortedDescending()
[3, 2, 1]

sortedBysortedByDescending

可變集合MutableList的排序操作。根據函數映射的結果進行升序排序和降序排序。
這兩個函數定義如下:

public inline fun <T, R : Comparable<R>> MutableList<T>.sortBy(crossinline selector: (T) -> R?): Unit {
    if (size > 1) sortWith(compareBy(selector))
}
public inline fun <T, R : Comparable<R>> MutableList<T>.sortByDescending(crossinline selector: (T) -> R?): Unit {
    if (size > 1) sortWith(compareByDescending(selector))
}

代碼示例

>>> val mlist = mutableListOf("abc","c","bn","opqde","")
>>> mlist.sortBy({it.length})
>>> mlist
[, c, bn, abc, opqde]
>>> mlist.sortByDescending({it.length})
>>> mlist
[opqde, abc, bn, c, ]

5.3.10 生產操作符

zip(other: Iterable<R>): List<Pair<T, R>>

兩個集合按照下標配對,組合成的每個Pair作爲新的List集合中的元素,並返回。

如果兩個集合長度不一樣,取短的長度。

代碼示例

>>> val list1 = listOf(1,2,3)
>>> val list2 = listOf(4,5,6,7)
>>> val list3 = listOf("x","y","z")
>>> list1.zip(list3)
[(1, x), (2, y), (3, z)]
>>> list3.zip(list1)
[(x, 1), (y, 2), (z, 3)]
>>> list2.zip(list3)
[(4, x), (5, y), (6, z)]  // 取短的長度
>>> list3.zip(list2)
[(x, 4), (y, 5), (z, 6)]
>>> list1.zip(listOf<Int>())
[]

這個zip函數的定義如下:

public infix fun <T, R> Iterable<T>.zip(other: Iterable<R>): List<Pair<T, R>> {
    return zip(other) { t1, t2 -> t1 to t2 }
}

我們可以看出,其內部是調用了zip(other) { t1, t2 -> t1 to t2 }。這個函數定義如下:

public inline fun <T, R, V> Iterable<T>.zip(other: Iterable<R>, transform: (a: T, b: R) -> V): List<V> {
    val first = iterator()
    val second = other.iterator()
    val list = ArrayList<V>(minOf(collectionSizeOrDefault(10), other.collectionSizeOrDefault(10)))
    while (first.hasNext() && second.hasNext()) {
        list.add(transform(first.next(), second.next()))
    }
    return list
}

依次取兩個集合相同索引的元素,使用提供的轉換函數transform得到映射之後的值,作爲元素組成一個新的List,並返回該List。列表的長度取兩個集合中最短的。

代碼示例

>>> val list1 = listOf(1,2,3)
>>> val list2 = listOf(4,5,6,7)
>>> val list3 = listOf("x","y","z")
>>> list1.zip(list3, {t1,t2 -> t2+t1})
[x1, y2, z3]
>>> list1.zip(list2, {t1,t2 -> t1*t2})
[4, 10, 18]

unzip(): Pair<List<T>, List<R>>

首先這個函數作用在元素是Pair的集合類上。依次取各個Pair元素的first, second值,分別放到List、List中,然後返回一個first爲List,second爲List的大的Pair。

函數定義

public fun <T, R> Iterable<Pair<T, R>>.unzip(): Pair<List<T>, List<R>> {
    val expectedSize = collectionSizeOrDefault(10)
    val listT = ArrayList<T>(expectedSize)
    val listR = ArrayList<R>(expectedSize)
    for (pair in this) {
        listT.add(pair.first)
        listR.add(pair.second)
    }
    return listT to listR
}

看到這裏,仍然有點抽象,我們直接看代碼示例:

>>> val listPair = listOf(Pair(1,2),Pair(3,4),Pair(5,6))
>>> listPair
[(1, 2), (3, 4), (5, 6)]
>>> listPair.unzip()
([1, 3, 5], [2, 4, 6])

partition(predicate: (T) -> Boolean): Pair<List<T>, List<T>>

根據判斷條件是否成立,將集合拆分成兩個子集合組成的 Pair。我們可以直接看函數的定義來更加清晰的理解這個函數的功能:

public inline fun <T> Iterable<T>.partition(predicate: (T) -> Boolean): Pair<List<T>, List<T>> {
    val first = ArrayList<T>()
    val second = ArrayList<T>()
    for (element in this) {
        if (predicate(element)) {
            first.add(element)
        } else {
            second.add(element)
        }
    }
    return Pair(first, second)
}

我們可以看出,這是一個內聯函數。

代碼示例

>>> val list = listOf(1,2,3,4,5,6,7,8,9)
>>> list
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list.partition({it>5})
([6, 7, 8, 9], [1, 2, 3, 4, 5])

plus(elements: Iterable<T>): List<T>

合併兩個List。

函數定義

public operator fun <T> Iterable<T>.plus(elements: Iterable<T>): List<T> {
    if (this is Collection) return this.plus(elements)
    val result = ArrayList<T>()
    result.addAll(this)
    result.addAll(elements)
    return result
}

我們可以看出,這是一個操作符函數。可以用”+”替代 。

代碼示例

>>> val list1 = listOf(1,2,3)
>>> val list2 = listOf(4,5)
>>> list1.plus(list2)
[1, 2, 3, 4, 5]
>>> list1+list2
[1, 2, 3, 4, 5]

關於plus函數還有以下的重載函數:

plus(element: T): List<T>
plus(elements: Array<out T>): List<T>
plus(elements: Sequence<T>): List<T>

等。

plusElement(element: T): List<T>

在集合中添加一個元素。
函數定義

@kotlin.internal.InlineOnly
public inline fun <T> Iterable<T>.plusElement(element: T): List<T> {
    return plus(element)
}

我們可以看出,這個函數內部是直接調用的plus(element: T): List<T>

代碼示例

>>> list1 + 10
[1, 2, 3, 10]
>>> list1.plusElement(10)
[1, 2, 3, 10]
>>> list1.plus(10)
[1, 2, 3, 10]

5.4 Set

類似的,Kotlin中的Set也分爲:不可變Set和支持增加和刪除的可變MutableSet。

不可變Set同樣是繼承了Collection。MutableSet接口繼承於Set, MutableCollection,同時對Set進行擴展,添加了對元素添加和刪除等操作。

Set的類圖結構如下:

img

5.4.1 空集

萬物生於無。我們先來看下Kotlin中的空集:

internal object EmptySet : Set<Nothing>, Serializable {
    private const val serialVersionUID: Long = 3406603774387020532

    override fun equals(other: Any?): Boolean = other is Set<*> && other.isEmpty()
    override fun hashCode(): Int = 0
    override fun toString(): String = "[]"

    override val size: Int get() = 0
    override fun isEmpty(): Boolean = true
    override fun contains(element: Nothing): Boolean = false
    override fun containsAll(elements: Collection<Nothing>): Boolean = elements.isEmpty()

    override fun iterator(): Iterator<Nothing> = EmptyIterator

    private fun readResolve(): Any = EmptySet
}

空集繼承了Serializable,表明是可被序列化的。它的size是0, isEmpty()返回true,hashCode()也是0。

下面是創建一個空集的代碼示例:

>>> val emptySet = emptySet<Int>()
>>> emptySet
[]
>>> emptySet.size
0
>>> emptySet.isEmpty()
true
>>> emptySet.hashCode()
0

5.4.2 創建Set

setOf

首先,Set中的元素是不可重複的(任意兩個元素 x, y 都不相等)。這裏的元素 x, y 不相等的意思是:

x.hashCode() != y.hashCode() 
!x.equals(y) 

上面兩個表達式值都爲true 。

代碼示例

>>> val list = listOf(1,1,2,3,3)
>>> list
[1, 1, 2, 3, 3]
>>> val set = setOf(1,1,2,3,3)
>>> set
[1, 2, 3]

Kotlin跟Java一樣的,判斷兩個對象的是否重複標準是hashCode()和equals()兩個參考值,也就是說只有兩個對象的hashCode值一樣與equals()爲真時,才認爲是相同的對象。所以自定義的類必須要要重寫hashCode()和equals()兩個函數。作爲Java程序員,這裏一般都會注意到。

創建多個元素的Set使用的函數是

setOf(vararg elements: T): Set<T> = if (elements.size > 0) elements.toSet() else emptySet()

這個toSet()函數是Array類的擴展函數,定義如下

public fun <T> Array<out T>.toSet(): Set<T> {
    return when (size) {
        0 -> emptySet()
        1 -> setOf(this[0])
        else -> toCollection(LinkedHashSet<T>(mapCapacity(size)))
    }
}

我們可以看出,setOf函數背後實際上用的是LinkedHashSet構造函數。關於創建Set的初始容量的算法是:

@PublishedApi
internal fun mapCapacity(expectedSize: Int): Int {
    if (expectedSize < 3) {
        return expectedSize + 1
    }
    if (expectedSize < INT_MAX_POWER_OF_TWO) {
        return expectedSize + expectedSize / 3
    }
    return Int.MAX_VALUE // 2147483647, any large value
}

也就是說,當元素個數n小於3,初始容量爲n+1;
當元素個數n小於2147483647 / 2 + 1 , 初始容量爲 n + n/3;
否則,初始容量爲2147483647

如果我們想對一個List去重,可以直接使用下面的方式

>>> list.toSet()
[1, 2, 3]

上文我們使用emptySet<Int>()來創建空集,我們也可以使用setOf()來創建空集:

>>> val s = setOf<Int>()
>>> s
[]

創建1個元素的Set:

>>> val s = setOf<Int>(1)
>>> s
[1]

這個函數調用的是setOf(element: T): Set<T> = java.util.Collections.singleton(element), 也是Java的Collections類裏的方法。

mutableSetOf(): MutableSet<T>

創建一個可變Set。

函數定義

@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> mutableSetOf(): MutableSet<T> = LinkedHashSet()

這個LinkedHashSet()構造函數背後實際上是java.util.LinkedHashSet<E>, 這就是Kotlin中的類型別名。

5.4.3 使用Java中的Set類

包kotlin.collections下面的TypeAliases.kt類中,有一些類型別名的定義如下:

@file:kotlin.jvm.JvmVersion

package kotlin.collections

@SinceKotlin("1.1") public typealias RandomAccess = java.util.RandomAccess


@SinceKotlin("1.1") public typealias ArrayList<E> = java.util.ArrayList<E>
@SinceKotlin("1.1") public typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
@SinceKotlin("1.1") public typealias HashMap<K, V> = java.util.HashMap<K, V>
@SinceKotlin("1.1") public typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
@SinceKotlin("1.1") public typealias HashSet<E> = java.util.HashSet<E>


// also @SinceKotlin("1.1")
internal typealias SortedSet<E> = java.util.SortedSet<E>
internal typealias TreeSet<E> = java.util.TreeSet<E>

從這裏,我們可以看出,Kotlin中的LinkedHashSet , HashSet, SortedSet, TreeSet 就是直接使用的Java中的對應的集合類。

對應的創建的方法是

hashSetOf
linkedSetOf
mutableSetOf
sortedSetOf

代碼示例如下:

>>> val hs = hashSetOf(1,3,2,7)
>>> hs
[1, 2, 3, 7]
>>> hs::class
class java.util.HashSet
>>> val ls = linkedSetOf(1,3,2,7)
>>> ls
[1, 3, 2, 7]
>>> ls::class
class java.util.LinkedHashSet
>>> val ms = mutableSetOf(1,3,2,7)
>>> ms
[1, 3, 2, 7]
>>> ms::class
class java.util.LinkedHashSet
>>> val ss = sortedSetOf(1,3,2,7)
>>> ss
[1, 2, 3, 7]
>>> ss::class
class java.util.TreeSet

我們知道在Java中,Set接口有兩個主要的實現類HashSet和TreeSet:

HashSet : 該類按照哈希算法來存取集合中的對象,存取速度較快。
TreeSet : 該類實現了SortedSet接口,能夠對集合中的對象進行排序。
LinkedHashSet:具有HashSet的查詢速度,且內部使用鏈表維護元素的順序,在對Set元素進行頻繁插入、刪除的場景中使用。

Kotlin並沒有單獨去實現一套HashSet、TreeSet和LinkedHashSet。如果我們在實際開發過程中,需要用到這些Set, 就可以直接用上面的方法。

5.4.4 Set元素的加減操作 plus minus

Kotlin中針對Set做了一些加減運算的擴展函數, 例如:

operator fun <T> Set<T>.plus(element: T)
plusElement(element: T)
plus(elements: Iterable<T>)
operator fun <T> Set<T>.minus(element: T)
minusElement(element: T)
minus(elements: Iterable<T>)

代碼示例:

>>> val ms = mutableSetOf(1,3,2,7)
>>> ms+10
[1, 3, 2, 7, 10]
>>> ms-1
[3, 2, 7]
>>> 
>>> ms + listOf(8,9)
[1, 3, 2, 7, 8, 9]
>>> ms - listOf(8,9)
[1, 3, 2, 7]
>>> ms - listOf(1,3)
[2, 7]

5.5 Map

5.5.1 Map概述

Map是一種把鍵對象Key和值對象Value映射的集合,它的每一個元素都包含一對鍵對象和值對象(K-V Pair)。 Key可以看成是Value 的索引,作爲key的對象在集合中不可重複(uniq)。

如果我們從數據結構的本質上來看,其實List就是Key是Int類型下標的特殊的Map。而Set也是Key爲Int,但是Value值不能重複的特殊Map。

Kotlin中的Map與List、Set一樣,Map也分爲只讀Map和可變的MutableMap。

Map沒有繼承於Collection接口。其類圖結構如下:

img

在接口interface Map<K, out V>中,K是鍵值的類型,V是對應的映射值的類型。這裏的out V表示類型爲V或V的子類。這是泛型的相關知識,我們將在下一章節中介紹。

其中,Entry<out K, out V>中保存的是Map的鍵值對。

5.5.2 創建Map

跟Java相比不同的是,在Kotlin中的Map區分了只讀的Map和可編輯的Map(MutableMap、HashMap、LinkedHashMap)。

Kotlin沒有自己重新去實現一套集合類,而是在Java的集合類基礎上做了一些擴展。

我們知道在Java中,根據內部數據結構的不同,Map 接口通常有多種實現類。

其中常用的有:

  • HashMap

HashMap是基於哈希表(hash table)的 Map 接口的實現,以key-value的形式存在。在HashMap中,key-value是一個整體,系統會根據hash算法來來計算key-value的存儲位置,我們可以通過key快速地存取value。它允許使用 null 值和 null 鍵。

另外,HashMap中元素的順序,隨着時間的推移會發生變化。

  • TreeMap

使用紅黑二叉樹(red-black tree)的 Map 接口的實現。

  • LinkedHashMap

還有繼承了HashMap,並使用鏈表實現的LinkedHashMap。LinkedHashMap保存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先得到的記錄是先插入的記錄。簡單說,LinkedHashMap是有序的,它使用鏈表維護內部次序。

我們在使用Kotlin創建Map的時候,實際上大部分都是調用Java的Map的方法。

下面我們就來介紹Map的創建以及基本操作函數。

mapOf()

創建一個只讀空Map。

>>> val map1 = mapOf<String, Int>()
>>> map1.size
0
>>> map1.isEmpty()
true

我們還可以用另外一個函數創建空Map:

>>> val map2 = emptyMap<String, Int>()
>>> map2.size
0
>>> map2.isEmpty()
true

空Map都是相等的:

>>> map2==map1
true

這個空Map是隻讀的,其屬性和函數返回都是預定義好的。其代碼如下:

private object EmptyMap : Map<Any?, Nothing>, Serializable {
    private const val serialVersionUID: Long = 8246714829545688274

    override fun equals(other: Any?): Boolean = other is Map<*,*> && other.isEmpty()
    override fun hashCode(): Int = 0
    override fun toString(): String = "{}"

    override val size: Int get() = 0
    override fun isEmpty(): Boolean = true

    override fun containsKey(key: Any?): Boolean = false
    override fun containsValue(value: Nothing): Boolean = false
    override fun get(key: Any?): Nothing? = null
    override val entries: Set<Map.Entry<Any?, Nothing>> get() = EmptySet
    override val keys: Set<Any?> get() = EmptySet
    override val values: Collection<Nothing> get() = EmptyList

    private fun readResolve(): Any = EmptyMap
}

mapOf(pair: Pair<K, V>): Map<K, V>

使用二元組Pair創建一個只讀Map。

>>> val map = mapOf(1 to "x", 2 to "y", 3 to "z")
>>> map
{1=x, 2=y, 3=z}
>>> map.get(1)
x
>>> map.get(3)
z
>>> map.size
3
>>> map.entries
[1=x, 2=y, 3=z]

這個創建函數內部是調用的LinkedHashMap構造函數,其相關代碼如下:

pairs.toMap(LinkedHashMap(mapCapacity(pairs.size)))

如果我們想編輯這個Map, 編譯器會直接報錯

>>> map[1]="a"
error: unresolved reference. None of the following candidates is applicable because of receiver type mismatch: 
@InlineOnly public operator inline fun <K, V> MutableMap<Int, String>.set(key: Int, value: String): Unit defined in kotlin.collections
@InlineOnly public operator inline fun kotlin.text.StringBuilder /* = java.lang.StringBuilder */.set(index: Int, value: Char): Unit defined in kotlin.text
map[1]="a"
^
error: no set method providing array access
map[1]="a"
   ^

因爲在不可變(Immutable)Map中,根本就沒有提供set函數。

mutableMapOf()

創建一個空的可變的Map。

>>> val map = mutableMapOf<Int, Any?>()
>>> map.isEmpty()
true
>>> map[1] = "x"
>>> map[2] = 1
>>> map
{1=x, 2=1}

該函數直接是調用的LinkedHashMap()構造函數。

mutableMapOf(vararg pairs: Pair<K, V>): MutableMap<K, V>

創建一個可編輯的MutableMap對象。

>>> val map = mutableMapOf(1 to "x", 2 to "y", 3 to "z")
>>> map
{1=x, 2=y, 3=z}
>>> map[1]="a"
>>> map
{1=a, 2=y, 3=z}

另外,如果Map中有重複的key鍵,後面的會直接覆蓋掉前面的:

>>> val map = mutableMapOf(1 to "x", 2 to "y", 1 to "z")
>>> map
{1=z, 2=y}

後面的1 to "z"直接把前面的1 to "x"覆蓋掉了。

hashMapOf(): HashMap<K, V>

創建HashMap對象。Kotlin直接使用的是Java的HashMap。

>>> val map: HashMap<Int, String> = hashMapOf(1 to "x", 2 to "y", 3 to "z")
>>> map
{1=x, 2=y, 3=z}

linkedMapOf(): LinkedHashMap<K, V>

創建空對象LinkedHashMap。直接使用的是Java中的LinkedHashMap。

>>> val map: LinkedHashMap<Int, String> = linkedMapOf()
>>> map
{}
>>> map[1]="x"
>>> map
{1=x}

linkedMapOf(vararg pairs: Pair<K, V>): LinkedHashMap<K, V>

創建帶二元組Pair元素的LinkedHashMap對象。直接使用的是Java中的LinkedHashMap。

>>> val map: LinkedHashMap<Int, String> = linkedMapOf(1 to "x", 2 to "y", 3 to "z")
>>> map
{1=x, 2=y, 3=z}
>>> map[1]="a"
>>> map
{1=a, 2=y, 3=z}

sortedMapOf(vararg pairs: Pair<K, V>): SortedMap<K, V>

創建一個根據Key升序排序好的TreeMap。對應的是使用Java中的SortedMap。

>>> val map = sortedMapOf(Pair("c", 3), Pair("b", 2), Pair("d", 1))
>>> map
{b=2, c=3, d=1}

5.5.3 訪問Map的元素

entries屬性

我們可以直接訪問entries屬性

val entries: Set<Entry<K, V>>

獲取該Map中的所有鍵/值對的Set。這個Entry類型定義如下:

 public interface Entry<out K, out V> {
        public val key: K
        public val value: V
    }

代碼示例

>>> val map = mapOf("x" to 1, "y" to 2, "z" to 3)
>>> map
{x=1, y=2, z=3}
>>> map.entries
[x=1, y=2, z=3]

這樣,我們就可以遍歷這個Entry的Set了:

>>> map.entries.forEach({println("key="+ it.key + " value=" + it.value)})
key=x value=1
key=y value=2
key=z value=3

keys屬性

訪問keys屬性:

val keys: Set<K>

獲取Map中的所有鍵的Set。

>>> map.keys
[x, y, z]

values屬性

訪問val values: Collection<V>獲取Map中的所有值的Collection。這個值的集合可能包含重複值。

>>> map.values
[1, 2, 3]

size屬性

訪問val size: Int獲取map鍵/值對的數目。

>>> map.size
3

get(key: K)

我們使用get函數來通過key來獲取value的值。

operator fun get(key: K): V?

對應的操作符是[]

>>> map["x"]
1
>>> map.get("x")
1

如果這個key不在Map中,就返回null。

>>> map["k"]
null

如果不想返回null,可以使用getOrDefault函數

getOrDefault(key: K, defaultValue: @UnsafeVariance V): V

當爲null時,不返回null,而是返回設置的一個默認值:

>>> map.getOrDefault("k",0)
0

這個默認值的類型,要和V對應。類型不匹配會報錯:

>>> map.getOrDefault("k","a")
error: type mismatch: inferred type is String but Int was expected
map.getOrDefault("k","a")
                     ^

5.5.4 Map操作符函數

containsKey(key: K): Boolean

是否包含該key。

>>> val map = mapOf("x" to 1, "y" to 2, "z" to 3)
>>> map.containsKey("x")
true
>>> map.containsKey("j")
false

containsValue(value: V): Boolean

是否包含該value。

>>> val map = mapOf("x" to 1, "y" to 2, "z" to 3)
>>> map.containsValue(2)
true
>>> map.containsValue(20)
false

component1() component2()

Map.Entry<K, V>的操作符函數,分別用來直接訪問key和value。

>>> val map = mapOf("x" to 1, "y" to 2, "z" to 3)
>>> map.entries.forEach({println("key="+ it.component1() + " value=" + it.component2())})
key=x value=1
key=y value=2
key=z value=3

這兩個函數的定義如下:

@kotlin.internal.InlineOnly
public inline operator fun <K, V> Map.Entry<K, V>.component1(): K = key

@kotlin.internal.InlineOnly
public inline operator fun <K, V> Map.Entry<K, V>.component2(): V = value

Map.Entry<K, V>.toPair(): Pair<K, V>

把Map的Entry轉換爲Pair。

>>> map.entries
[x=1, y=2, z=3]
>>> map.entries.forEach({println(it.toPair())})
(x, 1)
(y, 2)
(z, 3)

getOrElse(key: K, defaultValue: () -> V): V

通過key獲取值,當沒有值可以設置默認值。

>>> val map = mutableMapOf<String, Int?>()
>>> map.getOrElse("x", { 1 })
1
>>> map["x"] = 3
>>> map.getOrElse("x", { 1 })
3

getValue(key: K): V

當Map中不存在這個key,調用get函數,如果不想返回null,直接拋出異常,可調用此方法。

val map = mutableMapOf<String, Int?>()
>>> map.get("v")
null
>>> map.getValue("v")
java.util.NoSuchElementException: Key v is missing in the map.
    at kotlin.collections.MapsKt__MapWithDefaultKt.getOrImplicitDefaultNullable(MapWithDefault.kt:19)
    at kotlin.collections.MapsKt__MapsKt.getValue(Maps.kt:252)

getOrPut(key: K, defaultValue: () -> V): V

如果不存在這個key,就添加這個key到Map中,對應的value是defaultValue。

>>> val map = mutableMapOf<String, Int?>()
>>> map.getOrPut("x", { 2 })
2
>>> map
{x=2}

iterator(): Iterator<Map.Entry<K, V>>

這個函數返回的是 entries.iterator()。這樣我們就可以像下面這樣使用for循環來遍歷Map:

>>> val map = mapOf("x" to 1, "y" to 2, "z" to 3 )
>>> for((k,v) in map){println("key=$k, value=$v")}
key=x, value=1
key=y, value=2
key=z, value=3

mapKeys(transform: (Map.Entry<K, V>) -> R): Map<R, V>

把Map的Key設置爲通過轉換函數transform映射之後的值。

>>> val map:Map<Int,String> = mapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> val mmap = map.mapKeys{it.key * 10}
>>> mmap
{10=a, 20=b, 30=c, -10=z}

注意,這裏的it是Map的Entry。
如果不巧,有任意兩個key通過映射之後相等了,那麼後面的key將會覆蓋掉前面的key。

>>> val mmap = map.mapKeys{it.key * it.key}
>>> mmap
{1=z, 4=b, 9=c}

我們可以看出,1 to "a"-1 to "z"覆蓋掉了。

mapValues(transform: (Map.Entry<K, V>) -> R): Map<K, R>

對應的這個函數是把Map的value設置爲通過轉換函數transform轉換之後的新值。

>>> val map:Map<Int,String> = mapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> val mmap = map.mapValues({it.value + "$"})
>>> mmap
{1=a$, 2=b$, 3=c$, -1=z$}

filterKeys(predicate: (K) -> Boolean): Map<K, V>

返回過濾出滿足key判斷條件的元素組成的新Map。

>>> val map:Map<Int,String> = mapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> map.filterKeys({it>0})
{1=a, 2=b, 3=c}

注意,這裏的it元素是Key。

filterValues(predicate: (V) -> Boolean): Map<K, V>

返回過濾出滿足value判斷條件的元素組成的新Map。

>>> val map:Map<Int,String> = mapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> map.filterValues({it>"b"})
{3=c, -1=z}

注意,這裏的it元素是value。

filter(predicate: (Map.Entry<K, V>) -> Boolean): Map<K, V>

返回過濾出滿足Entry判斷條件的元素組成的新Map。

>>> val map:Map<Int,String> = mapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> map.filter({it.key>0 && it.value > "b"})
{3=c}

Iterable<Pair<K, V>>.toMap(destination: M): M

把持有Pair的Iterable集合轉換爲Map。

>>> val pairList = listOf(Pair(1,"a"),Pair(2,"b"),Pair(3,"c"))
>>> pairList
[(1, a), (2, b), (3, c)]
>>> pairList.toMap()
{1=a, 2=b, 3=c}

Map<out K, V>.toMutableMap(): MutableMap<K, V>

把一個只讀的Map轉換爲可編輯的MutableMap。

>>> val map = mapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> map[1]="x"
error: unresolved reference. None of the following candidates is applicable ...
error: no set method providing array access
map[1]="x"
   ^

>>> val mutableMap = map.toMutableMap()
>>> mutableMap
{1=a, 2=b, 3=c, -1=z}
>>> mutableMap[1]="x"
>>> mutableMap
{1=x, 2=b, 3=c, -1=z}

plus minus

Map的加法運算符函數如下:

operator fun <K, V> Map<out K, V>.plus(pair: Pair<K, V>): Map<K, V>
operator fun <K, V> Map<out K, V>.plus(pairs: Iterable<Pair<K, V>>): Map<K, V>
operator fun <K, V> Map<out K, V>.plus(pairs: Array<out Pair<K, V>>): Map<K, V>
operator fun <K, V> Map<out K, V>.plus(pairs: Sequence<Pair<K, V>>): Map<K, V>
operator fun <K, V> Map<out K, V>.plus(map: Map<out K, V>): Map<K, V>

代碼示例:

>>> val map = mapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> map+Pair(10,"g")
{1=a, 2=b, 3=c, -1=z, 10=g}
>>> map + listOf(Pair(9,"s"),Pair(10,"w"))
{1=a, 2=b, 3=c, -1=z, 9=s, 10=w}
>>> map + arrayOf(Pair(9,"s"),Pair(10,"w"))
{1=a, 2=b, 3=c, -1=z, 9=s, 10=w}
>>> map + sequenceOf(Pair(9,"s"),Pair(10,"w"))
{1=a, 2=b, 3=c, -1=z, 9=s, 10=w}
>>> map + mapOf(9 to "s", 10 to "w")
{1=a, 2=b, 3=c, -1=z, 9=s, 10=w}

加並賦值函數:

inline operator fun <K, V> MutableMap<in K, in V>.plusAssign(pair: Pair<K, V>)
inline operator fun <K, V> MutableMap<in K, in V>.plusAssign(pairs: Iterable<Pair<K, V>>)
inline operator fun <K, V> MutableMap<in K, in V>.plusAssign(pairs: Array<out Pair<K, V>>)
inline operator fun <K, V> MutableMap<in K, in V>.plusAssign(pairs: Sequence<Pair<K, V>>)
inline operator fun <K, V> MutableMap<in K, in V>.plusAssign(map: Map<K, V>)

代碼示例:

>>> val map = mutableMapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> map+=Pair(10,"g")
>>> map
{1=a, 2=b, 3=c, -1=z, 10=g}
>>> map += listOf(Pair(9,"s"),Pair(11,"w"))
>>> map
{1=a, 2=b, 3=c, -1=z, 10=g, 9=s, 11=w}
>>> map += mapOf(20 to "qq", 30 to "tt")
>>> map
{1=a, 2=b, 3=c, -1=z, 10=g, 9=s, 11=w, 20=qq, 30=tt}

減法跟加法類似。

put(key: K, value: V): V?

根據key設置元素的value。如果該key存在就更新value;不存在就添加,但是put的返回值是null。

>>> val map = mutableMapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> map
{1=a, 2=b, 3=c, -1=z}
>>> map.put(10,"q")
null
>>> map
{1=a, 2=b, 3=c, -1=z, 10=q}
>>> map.put(1,"f")
a
>>> map
{1=f, 2=b, 3=c, -1=z, 10=q}

putAll(from: Map<out K, V>): Unit

把一個Map全部添加到一個MutableMap中。

>>> val map = mutableMapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> val map2 = mapOf(99 to "aa", 100 to "bb")
>>> map.putAll(map2)
>>> map
{1=a, 2=b, 3=c, -1=z, 99=aa, 100=bb}

如果有key重複的,後面的值會覆蓋掉前面的值:

>>> map
{1=a, 2=b, 3=c, -1=z, 99=aa, 100=bb}
>>> map.putAll(mapOf(1 to "www",2 to "tttt"))
>>> map
{1=www, 2=tttt, 3=c, -1=z, 99=aa, 100=bb}

MutableMap<out K, V>.remove(key: K): V?

根據鍵值key來刪除元素。

>>> val map = mutableMapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> map.remove(-1)
z
>>> map
{1=a, 2=b, 3=c}
>>> map.remove(100)
null
>>> map
{1=a, 2=b, 3=c}

MutableMap<K, V>.clear(): Unit

清空MutableMap。

>>> val map = mutableMapOf(1 to "a", 2 to "b", 3 to "c", -1 to "z")
>>> map
{1=a, 2=b, 3=c, -1=z}
>>> map.clear()
>>> map
{}

本章小結

本章我們介紹了Kotlin標準庫中的集合類List、Set、Map,以及它們擴展的豐富的操作函數,這些函數使得我們使用這些集合類更加簡單容易。

集合類持有的是對象,而怎樣的放入正確的對象類型則是我們寫代碼過程中需要注意的。下一章節中我們將學習泛型。

發佈了273 篇原創文章 · 獲贊 266 · 訪問量 123萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章