Kotlin 集合概述
Kotlin 集合提供了對數據進行各種操作的便捷方式。它們實現了接口,因此可以操作不同類型的數據。例如,你可以編寫一個函數,同時打印 Set
和 List
的所有元素。我們來看看這是如何實現的。
Iterable 接口
我們已經知道,Kotlin 中有兩個通用接口:Iterable
和 MutableIterable
,它們提供了一系列用于處理元素序列的方法。
繼承自 Iterable
的類擁有一組通用方法,例如:
-
iterator()
-
forEach()
-
contains()
-
count()
-
drop()
-
filter()
等。
而 MutableIterable
除了擁有 Iterable
的方法外,還支持迭代時移除元素的方法,例如:
-
remove()
-
removeAll()
。
值得注意的是,雖然 drop()
和 remove()
都可以“移除”元素,但它們的行為不同:
-
drop()
返回的是一個新的集合對象。 -
remove()
會直接修改原集合對象(前提是集合是可變的)。
還要注意:你只能使用同一個 iterator
對象遍歷一次集合,如果想再遍歷一次,就必須重新創建一個新的 iterator()
。
當然你也可以使用 for
循環或者 forEach()
來迭代集合。
Collection 接口結構
我們來看看 Kotlin 集合之間的繼承關系:
-
Iterable<T>
←Collection<T>
←List<T>
或Set<T>
-
MutableIterable<T>
←MutableCollection<T>
←MutableList<T>
或MutableSet<T>
其中:
-
Collection<T>
是不可變集合的根接口,支持size
、get()
、find()
、filter()
、count()
等方法。 -
MutableCollection<T>
是可變集合的根接口,支持add()
、remove()
、drop()
、addAll()
等方法。
注意:雖然我們通常也將 Map
稱作集合,但它并不繼承自 Collection
或 Iterable
,因為它存儲的是鍵值對而不是單個元素,所以它有自己獨特的方法集合。
使用 Collection 接口舉例
你可以通過三種方式遍歷集合:
fun printAll(strings: Collection<String>) {for (str in strings) print("$str ")
}fun printAll(songs: Collection<String>) {songs.forEach { print("$it ") }
}fun printAll(songs: Collection<String>) {val songsIterator = songs.iterator()while (songsIterator.hasNext()) {print("${songsIterator.next()} ")}
}
示例:
val listOfSongs = listOf("Creep", "Idioteque", "Street Spirit", "Paranoid Android")
printAll(listOfSongs)
// 輸出:Creep Idioteque Street Spirit Paranoid Android
所有集合都有一些通用的方法:
-
count()
:返回滿足條件的元素數量。 -
drop()
:返回一個新的列表,其中不包含前 n 個元素。 -
containsAll()
:檢查集合是否包含另一個集合中的所有元素,并返回 true 或 false。
常用方法示例:
fun countElements(strings: Collection<String>) = strings.count { it.matches("\\w+".toRegex()) }fun dropElements(songs: Collection<String>) = songs.drop(2).toSet()fun compareCollections(old: Collection<String>, new: Collection<String>) = old.containsAll(new)val setOfSongs = setOf("Creep", "Idioteque", "Street Spirit", "Paranoid Android")
val listOfSongs = listOf("Creep", "Idioteque", "Street Spirit", "Paranoid Android")println(countElements(setOfSongs)) // 輸出:2
println(dropElements(listOfSongs)) // 輸出:[Street Spirit, Paranoid Android]
println(compareCollections(listOfSongs, setOfSongs)) // 輸出:true
更多集合操作方法
-
joinToString()
將集合作為帶有特定分隔符的字符串返回。 -
find()
返回符合模式的第一個元素。 -
filter()
返回List
滿足條件的元素。 -
minus()
返回不包含條件中指定的元素的集合。 -
random()
返回集合中的一個隨機元素。
一些例子:
fun convertToString(strings: Collection<String>) = strings.joinToString(" | ")fun findElement(strings: Collection<String>) = strings.find { it.contains("I") }fun filterElements(strings: Collection<String>) = strings.filter { it.contains("t") }fun returnRandomElement(strings: Collection<String>) = strings.random()fun decreaseCollection(strings: Collection<String>) = strings.minus("Creep")
示例:
val listOfSongs = listOf("Creep", "Idioteque", "Street Spirit", "Paranoid Android")
val setOfSongs = setOf("Creep", "Idioteque", "Street Spirit", "Paranoid Android")println(convertToString(listOfSongs)) // Creep | Idioteque | Street Spirit | Paranoid Android
println(findElement(setOfSongs)) // Idioteque
println(filterElements(listOfSongs)) // [Idioteque, Street Spirit]
println(returnRandomElement(setOfSongs)) // 可能輸出任意一個元素
println(decreaseCollection(setOfSongs)) // [Idioteque, Street Spirit, Paranoid Android]
注意:
drop()
和minus()
都不會修改原集合,而是返回一個新的集合對象。
MutableCollection 可變集合
MutableCollection
可以使用 Collection
的所有方法,還支持如下修改原集合的方法:
-
addAll()
:添加一整個集合。 -
add()
:添加單個元素。 -
clear()
:清空集合。 -
remove()
:移除元素的第一個出現位置。
fun addCollection(old: MutableCollection<String>, new: Collection<String>) {old.addAll(new)
}fun addNewElement(old: MutableCollection<String>) {old.add("Spectre")
}fun clearCollection(old: MutableCollection<String>) {old.clear()
}fun removeElement(old: MutableCollection<String>): Boolean {return old.remove("Creep")
}
示例:
val oldSongs = mutableSetOf("Creep", "Street Spirit")
val newSongs = listOf("Creep", "Street Spirit", "Paranoid Android")clearCollection(oldSongs)
println(oldSongs) // []addCollection(oldSongs, newSongs)
println(oldSongs) // [Creep, Street Spirit, Paranoid Android]addNewElement(oldSongs)
println(oldSongs) // [Creep, Street Spirit, Paranoid Android, Spectre]removeElement(oldSongs)
println(oldSongs) // [Street Spirit, Paranoid Android, Spectre]
println(removeElement(oldSongs)) // false,因為沒有 "Creep" 了
retainAll()
retainAll()
方法會讓集合只保留在另一個集合中也存在的元素:
fun retainAllFromCollection(old: MutableCollection<String>, new: Collection<String>) {old.retainAll(new)
}val oldSongs = mutableSetOf("Creep", "Street Spirit", "Paranoid Android")
val newSongs = listOf("Spectre", "Street Spirit")retainAllFromCollection(oldSongs, newSongs)
println(oldSongs) // [Street Spirit]
函數與集合的關系
你可以使用集合的接口類型(如 Collection
或 List
)作為函數的參數,以創建更通用的處理函數。例如:
fun processNumbers(list: List<Number>) {list.forEach { print("$it ") }
}val numbers1 = listOf(0, 12, 10)
val numbers2 = listOf(0.0, 12.0, 10.0)
val numbers3 = listOf(423324534536356, 4L, 56L)processNumbers(numbers1) // 0 12 10
processNumbers(numbers2) // 0.0 12.0 10.0
processNumbers(numbers3) // 423324534536356 4 56
但如果你嘗試在 Number
類型上做加法,會出錯:
fun processNumbers(list: List<Number>) {list.forEach { print(it + 1) } // ? 報錯:未解析的引用
}
因為 Number
是抽象類,不支持加法。你可以先轉換類型:
fun processNumbers(list: List<Number>) {list.forEach { print("${it.toChar()} ") }
}val numbers1 = listOf(41, 42, 43)
processNumbers(numbers1) // ) * +
總結
- Kotlin 中集合是接口,可以通用于多種集合類型。
Collection
和MutableCollection
提供了許多通用操作方法。Map
雖然被稱為集合,但并不繼承自Collection
。可以編寫通用函數處理Set
、List
、MutableSet
、MutableList
等集合類型。drop()
和minus()
不改變原集合,而remove()
、add()
會修改原集合(前提是集合是可變的)。