Kotlin系列之四-集合
一.Kotlin中的集合
1.相關(guān)集合類型:
-List:有序
1).var multableListOf=multableListOf(),可變集合鳄厌,不是型變的
2).var listOf=listOf(),只讀集合泪漂,型變的,在需要父類的地方可以傳入子類
-Set:唯一
-Map:鍵值對
2.集合的根
Collection
3.List在Kotlin的默認實現(xiàn)是ArrayList
4.可操作的List是MutableList敌卓,可以進行增,刪,改惧蛹,查
5.Set:
1.存儲的元素不可重,有唯一性
2.元素可以有且只能有1個null
3.MutableSet是帶有來自MutableCollection的寫操作接口的Set
4.Set的默認實現(xiàn)是:LinkedHashSet-保留原素的插入順序,所以first(),last()等函數(shù)可以拿到期望的元素
而另外一種HashSet是無序的像云,但是同樣多元素多的時候,它比LinkedHashSet占用更少的內(nèi)存
6.Map
1.Map并不是Collection的子類
2.該鍵值對侈贷,鍵必須是唯一的上遥,值可以重復(fù)
fun testMap() {
var map = mapOf<String, Int>("key1" to 1, "key2" to 2, "key3" to 3)
//取元素
var key1Value = map["key1"]
println("key1 對應(yīng)的元素值為 $key1Value")
println("all keys ${map.keys}")
println("all values ${map.values}")
if ("key2" in map) {
println("value by key2 is ${map["key2"]}")
}
if (1 in map.values||map.containsValue(1)){
println("1 is in map value")
}
}
3.map的默認實現(xiàn)是LinkedHashMap辣恋,有序,而另一種實現(xiàn)HashMap是無序的
7.構(gòu)造集合
1.使用Kotlin api也就是標(biāo)準(zhǔn)的庫函數(shù)
listOf<T>(),mapOf<T>(),setOf<T>(),mutableListOf<T>(),mutableSetOf<T>()
2.如果構(gòu)造時底靠,指定元素,那么編譯器會自動推測類型,但是創(chuàng)建空集合時雕凹,必須指定元素類型
3.map的創(chuàng)建時线欲,是接收了由to創(chuàng)建的Pair對象
注意,to僅在不注重性能的時候推薦使用,若想考慮性能嗜憔,請使用apply()函數(shù)
4.創(chuàng)建空集合:emptyList(),emptySet(),emptyMap()
5.List的初始化函數(shù)
val doubled = List(5, { it * 2 })
println(doubled)//[0, 2, 4, 6, 8]
6.集合的復(fù)制廓鞠,toListOf(),toMapOf(),toSetOf(),源集合的改變不會影響副本集合滋早,同樣,副本集合也獨立于源集合進行更改
有時也可以把集合轉(zhuǎn)化為其他類型:
val sourceList = mutableListOf(1, 2, 3)
var toSet = sourceList.toMutableSet()
toSet.add(999)
println(toSet) // [1, 2, 3, 999]
7.調(diào)用其他集合的函數(shù)
1).過濾
val numbers = listOf(1, 2, 4)
var filter = numbers.filter { it>3}
println(filter)//[4]
2).映射生成轉(zhuǎn)換結(jié)果列表:
var numbers = setOf(1, 2, 3)
var map = numbers.map { it + 1 }
println(map + "-" + map.javaClass)//[2, 3, 4, -, class java.util.ArrayList]
var mapIndexed = numbers.mapIndexed { idx, value ->
value * idx
}
println(mapIndexed + "-" + mapIndexed.javaClass) // [0, 2, 6, -, class java.util.ArrayList]
3).關(guān)聯(lián)生成map
val numbers = listOf("one", "two", "three", "four")
var message = numbers.associateWith { it.length }
println(message)//{one=3, two=3, three=5, four=4}
println(" " + message.javaClass)// class java.util.LinkedHashMap
8.迭代器
1.迭代器的常用機制:對象可按順序提供對元素的訪問權(quán)限,而不會暴露集合的底層結(jié)構(gòu)
2.List,Set繼承自Iterable<T>接口,可以使用iterator()方法獲取迭代器
val numbers = listOf(1, 2, 3, 4, 5)
var iterator = numbers.iterator()
while (iterator.hasNext()) {
println(iterator.next())
}
println("--------")
for (number in numbers) {
println(number)
}
println("--------")
numbers.forEach { println(it) }
3.List具有雙向迭代器ListIterator
val numbers = listOf(1, 2, 3, 4, 5)
var listIterator = numbers.listIterator()
while (listIterator.hasNext()) {
println("next Index is ${listIterator.nextIndex()} and next value is ${listIterator.next()}")
}
while (listIterator.hasPrevious()) {
println("previous index is ${listIterator.previousIndex()} and previous value is ${listIterator.previous()}")
}
//next Index is 1 and next value is 2
//next Index is 2 and next value is 3
//next Index is 3 and next value is 4
//next Index is 4 and next value is 5
//previous index is 4 and previous value is 5
//previous index is 3 and previous value is 4
//previous index is 2 and previous value is 3
//previous index is 1 and previous value is 2
//previous index is 0 and previous value is 1
4.可變迭代器:MutableIterator(刪除)/MutableListIterator(插入/替換)
var numbers = mutableListOf("1", "2", "3")
var iterator = numbers.iterator()
println(iterator.javaClass)
iterator.next()//刪掉這句話直接報錯
iterator.remove()
println("numbers after removed :$numbers")
9.區(qū)間與數(shù)列
1.區(qū)間
1).Kotlin通過kotlin.range保重的rangeTo()及其操作數(shù)形式的..創(chuàng)建兩個值的區(qū)間,通常rangeTo會輔以in或!in函數(shù)
var i = 3
if (i in 1..4) {
println(true)//true
}
2).要想反向迭代數(shù)字使用關(guān)鍵字downTo,而不是..
var i = 3
if (i in 4 downTo 0) {
println(true)//true
}
3).也可以通過任意步長迭代數(shù)字:
for (i in 1..4 step 2) {
println(i)//1,3
}
4).要想排除區(qū)間的某個數(shù),使用until
for (i in 1 until 4) {
println(i)//1赏半,2,3
}
2.數(shù)列
1.整數(shù)類型的區(qū)間可以視為等差數(shù)列:IntProgression踱葛、LongProgression 與 CharProgression甥材。
2.數(shù)列具有三個基本屬性:first 元素、last 元素和一個非零的 step 叠萍。
數(shù)列的 last 元素是這樣計算的:
— 對于正步?:不大于結(jié)束值且滿足 (last - first) % step == 0 的最大值。
— 對于負步?:不小于結(jié)束值且滿足 (last - first) % step == 0 的最小值。
for (i in 1..9 step 4){
println(i)//1,5,9
}
var message = (1..10).filter { it % 2 == 0 }
println(message)//[2, 4, 6, 8, 10]
10.序列
1.除了集合之外,Kotlin 標(biāo)準(zhǔn)庫還包含另一種容器類型?序列(Sequence<T>)自沧。序列提供與 Iterable 相同的函數(shù),但實現(xiàn)另一種方法來進行多步驟集合處理。
當(dāng) Iterable 的處理包含多個步驟時,它們會優(yōu)先執(zhí)行:每個處理步驟完成并返回其結(jié)果?中間集 合雨效。在此集合上執(zhí)行以下步驟。反過來传透,序列的多步處理在可能的情況下會延遲執(zhí)行:僅當(dāng)請求整個處理
鏈的結(jié)果時才進行實際計算。
操作執(zhí)行的順序也不同:Sequence 對每個元素逐個執(zhí)行所有處理步驟。反過來,Iterable 完成整 個集合的每個步驟清女,然后進行下一步传惠。
因此羊瘩,這些序列可避免生成中間步驟的結(jié)果,從而提高了整個集合處理鏈的性能。但是擒贸,序列的延遲性質(zhì) 增加了一些開銷,這些開銷在處理較小的集合或進行更簡單的計算時可能很重要险绘。因此翔烁,應(yīng)該同時考慮 使用 Sequence 與 Iterable,并確定在哪種情況更適合。
二.集合的操作
Kotlin 標(biāo)準(zhǔn)庫提供了用于對集合執(zhí)行操作的多種函數(shù)。這包括簡單的操作,例如獲取或添加元素霎箍,以及 更復(fù)雜的操作,包括搜索、排序、過濾骗随、轉(zhuǎn)換等。
擴展與成員函數(shù)
集合操作在標(biāo)準(zhǔn)庫中以兩種方式聲明:集合接口的成員函數(shù)和擴展函數(shù)。 成員函數(shù)定義了對于集合類型是必不可少的操作蚕冬。例如,Collection 包含函數(shù) isEmpty() 來檢查其
是 否 為 空 ; L i s t 包 含 用 于 對 元 素 進 行 索 引 訪 問 的 g e t ( ) 是辕,等 等 囤热。 創(chuàng)建自己的集合接口實現(xiàn)時,必須實現(xiàn)其成員函數(shù)获三。為了使新實現(xiàn)的創(chuàng)建更加容易,請使用標(biāo)準(zhǔn)庫中集
合接口的框架實現(xiàn):AbstractCollection疙教、AbstractList棺聊、AbstractSet、AbstractMap 及其 相應(yīng)可變抽象類贞谓。
其他集合操作被聲明為擴展函數(shù)限佩。這些是過濾、轉(zhuǎn)換裸弦、排序和其他集合處理功能祟同。
公共操作
公共操作可用于只讀集合與可變集合。常?操作分為以下幾類:
— 集合轉(zhuǎn)換
— 集合過濾
— plus 與 minus 操作符
— 分組
— 取集合的一部分
— 取單個元素
— 集合排序
— 集合聚合操作
這些?面中描述的操作將返回其結(jié)果理疙,而不會影響原始集合晕城。例如,一個過濾操作產(chǎn)生一個新集合沪斟,其 中包含與過濾謂詞匹配的所有元素广辰。此類操作的結(jié)果應(yīng)存儲在變量中,或以其他方式使用主之,例如择吊,傳到其 他函數(shù)中。
對于某些集合操作槽奕,有一個選項可以指定 目標(biāo) 對象几睛。目標(biāo)是一個可變集合,該函數(shù)將其結(jié)果項附加到該 可變對象中粤攒,而不是在新對象中返回它們所森。對于執(zhí)行帶有目標(biāo)的操作囱持,有單獨的函數(shù),其名稱中帶有 To
后綴焕济,例如纷妆,用 filterTo() 代替 filter() 以及用 associateTo() 代替 associate()。這些函 數(shù)將目標(biāo)集合作為附加參數(shù)晴弃。
val numbers = listOf("one", "two", "three", "four")
numbers.filter { it.length > 3 } // `numbers` 沒有任何改變掩幢,結(jié)果丟失 println("numbers are still $numbers")
val longerThan3 = numbers.filter { it.length > 3 } // 結(jié)果存儲在 `longerThan3` 中 println("numbers longer than 3 chars are $longerThan3")
val numbers = listOf("one", "two", "three", "four")
val filterResults = mutableListOf<String>() // 目標(biāo)對象 numbers.filterTo(filterResults) { it.length > 3 } numbers.filterIndexedTo(filterResults) { index, _ -> index == 0 } println(filterResults) // 包含兩個操作的結(jié)果
為了方便起?,這些函數(shù)將目標(biāo)集合返回了上鞠,因此您可以在函數(shù)調(diào)用的相應(yīng)參數(shù)中直接創(chuàng)建它:
具有目標(biāo)的函數(shù)可用于過濾际邻、關(guān)聯(lián)、分組芍阎、展平以及其他操作世曾。有關(guān)目標(biāo)操作的完整列表,請參? Kotlin collections reference谴咸。
寫操作
對于可變集合轮听,還存在可更改集合狀態(tài)的 寫操作 。這些操作包括添加寿冕、刪除和更新元素蕊程。寫操作在集合 寫操作以及 List 寫操作與 Map 寫操作的相應(yīng)部分中列出。
對于某些操作驼唱,有成對的函數(shù)可以執(zhí)行相同的操作:一個函數(shù)就地應(yīng)用該操作,另一個函數(shù)將結(jié)果作為 單獨的集合返回驹暑。例如玫恳,sort() 就地對可變集合進行排序,因此其狀態(tài)發(fā)生了變化;sorted() 創(chuàng)建
一個新集合优俘,該集合包含按排序順序相同的元素京办。
// 將數(shù)字直接過濾到新的哈希集中,
// 從而消除結(jié)果中的重復(fù)項
val result = numbers.mapTo(HashSet()) { it.length } println("distinct item lengths are $result")
val numbers = mutableListOf("one", "two", "three", "four") val sortedNumbers = numbers.sorted()
println(numbers == sortedNumbers) // false
numbers.sort()
println(numbers == sortedNumbers) // true
集合轉(zhuǎn)換
Kotlin 標(biāo)準(zhǔn)庫為集合 轉(zhuǎn)換 提供了一組擴展函數(shù)帆焕。這些函數(shù)根據(jù)提供的轉(zhuǎn)換規(guī)則從現(xiàn)有集合中構(gòu)建新集 合惭婿。在此?面中,我們將概述可用的集合轉(zhuǎn)換函數(shù)叶雹。
映射
映射 轉(zhuǎn)換從另一個集合的元素上的函數(shù)結(jié)果創(chuàng)建一個集合财饥。基本的映射函數(shù)是 map()折晦。它將給定的 lambda 函數(shù)應(yīng)用于每個后續(xù)元素钥星,并返回 lambda 結(jié)果列表。結(jié)果的順序與元素的原始順序相同满着。如
需應(yīng)用還要用到元素索引作為參數(shù)的轉(zhuǎn)換谦炒,請使用 mapIndexed()贯莺。
如果轉(zhuǎn)換在某些元素上產(chǎn)生 null 值,則可以通過調(diào)用 mapNotNull() 函數(shù)取代 map() 或 mapIndexedNotNull() 取代 mapIndexed() 來從結(jié)果集中過濾掉 null 值宁改。
映射轉(zhuǎn)換時缕探,有兩個選擇:轉(zhuǎn)換鍵,使值保持不變还蹲,反之亦然爹耗。要將指定轉(zhuǎn)換應(yīng)用于鍵,請使用 mapKeys();反過來秽誊,mapValues() 轉(zhuǎn)換值鲸沮。這兩個函數(shù)都使用將映射條目作為參數(shù)的轉(zhuǎn)換,因此可
以操作其鍵與值锅论。
雙路合并
雙路合并 轉(zhuǎn)換是根據(jù)兩個集合中具有相同位置的元素構(gòu)建配對讼溺。在 Kotlin 標(biāo)準(zhǔn)庫中,這是通過 zip() 擴展函數(shù)完成的最易。在一個集合(或數(shù)組)上以另一個集合(或數(shù)組)作為參數(shù)調(diào)用時怒坯,zip() 返回 Pair 對 象 的 列 表( L i s t )。接 收 者 集 合 的 元 素 是 這 些 配 對 中 的 第 一 個 元 素 藻懒。如 果 集 合 的 大 小 不 同 剔猿,則
zip() 的結(jié)果為較小集合的大小;結(jié)果中不包含較大集合的后續(xù)元素。zip() 也可以中綴形式調(diào)用 a zip b 嬉荆。
也可以使用帶有兩個參數(shù)的轉(zhuǎn)換函數(shù)來調(diào)用 zip() :接收者元素和參數(shù)元素归敬。在這種情況下,結(jié)果 List 包含在具有相同位置的接收者對和參數(shù)元素對上調(diào)用的轉(zhuǎn)換函數(shù)的返回值鄙早。
val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 }) println(numbers.mapIndexed { idx, value -> value * idx })
val numbers = setOf(1, 2, 3)
println(numbers.mapNotNull { if ( it == 2) null else it * 3 }) println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx })
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11) println(numbersMap.mapKeys { it.key.toUpperCase() }) println(numbersMap.mapValues { it.value + it.key.length })
val colors = listOf("red", "brown", "grey") val animals = listOf("fox", "bear", "wolf") println(colors zip animals)
val twoAnimals = listOf("fox", "bear") println(colors.zip(twoAnimals))
val colors = listOf("red", "brown", "grey") val animals = listOf("fox", "bear", "wolf")
println(colors.zip(animals) { color, animal -> "The ${animal.capitalize()} is $color"})
當(dāng)擁有 Pair 的 List 時汪茧,可以進行反向轉(zhuǎn)換 unzipping?從這些鍵值對中構(gòu)建兩個列表: — 第一個列表包含原始列表中每個 Pair 的鍵。
— 第二個列表包含原始列表中每個 Pair 的值限番。 要分割鍵值對列表舱污,請調(diào)用 unzip()。
關(guān)聯(lián)
關(guān)聯(lián) 轉(zhuǎn)換允許從集合元素和與其關(guān)聯(lián)的某些值構(gòu)建 Map弥虐。在不同的關(guān)聯(lián)類型中扩灯,元素可以是關(guān)聯(lián) Map 中的鍵或值。
基本的關(guān)聯(lián)函數(shù) associateWith() 創(chuàng)建一個 Map 霜瘪,其中原始集合的元素是鍵珠插,并通過給定的轉(zhuǎn)換函 數(shù)從中產(chǎn)生值。如果兩個元素相等粥庄,則僅最后一個保留在 Map 中丧失。
為了使用集合元素作為值來構(gòu)建 Map,有一個函數(shù) associateBy()惜互。它需要一個函數(shù)布讹,該函數(shù)根據(jù)元 素的值返回鍵琳拭。如果兩個元素相等,則僅最后一個保留在 Map 中描验。還可以使用值轉(zhuǎn)換函數(shù)來調(diào)用
associateBy() 白嘁。
另一種構(gòu)建 Map 的方法是使用函數(shù) associate(),其中 Map 鍵和值都是通過集合元素生成的膘流。它需 要一個 lambda 函數(shù)絮缅,該函數(shù)返回 Pair :鍵和相應(yīng) Map 條目的值。
請注意呼股,associate() 會生成臨時的 Pair 對象耕魄,這可能會影響性能。因此彭谁,當(dāng)性能不是很關(guān)鍵或比 其他選項更可取時吸奴,應(yīng)使用 associate() 。
后者的一個示例:從一個元素一起生成鍵和相應(yīng)的值缠局。
此時则奥,首先在一個元素上調(diào)用一個轉(zhuǎn)換函數(shù),然后根據(jù)該函數(shù)結(jié)果的屬性建立 Pair狭园。
val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4) println(numberPairs.unzip())
val numbers = listOf("one", "two", "three", "four") println(numbers.associateWith { it.length })
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateBy { it.first().toUpperCase() }) println(numbers.associateBy(keySelector = { it.first().toUpperCase() }, valueTransform = { it.length }))
val names = listOf("Alice Adams", "Brian Brown", "Clara Campbell") println(names.associate { name -> parseFullName(name).let { it.lastName to it.firstName } })
打平
如需操作嵌套的集合读处,則可能會發(fā)現(xiàn)提供對嵌套集合元素進行打平訪問的標(biāo)準(zhǔn)庫函數(shù)很有用。 第一個函數(shù)為 flatten()唱矛》2眨可以在一個集合的集合(例如,一個 Set 組成的 List )上調(diào)用它绎谦。該函數(shù)
返回嵌套集合中的所有元素的一個 List 馆匿。
另一個函數(shù)?flatMap() 提供了一種靈活的方式來處理嵌套的集合。它需要一個函數(shù)將一個集合元 素映射到另一個集合燥滑。因此,flatMap() 返回單個列表其中包含所有元素的值阿逃。所以铭拧,flatMap() 表現(xiàn)為 map()(以集合作為映射結(jié)果)與 flatten() 的連續(xù)調(diào)用。
字符串表示
如果需要以可讀格式檢索集合內(nèi)容恃锉,請使用將集合轉(zhuǎn)換為字符串的函數(shù):joinToString() 與 joinTo()搀菩。
joinToString() 根據(jù)提供的參數(shù)從集合元素構(gòu)建單個 String 。joinTo() 執(zhí)行相同的操作破托,但 將結(jié)果附加到給定的 Appendable 對象肪跋。
當(dāng)使用默認參數(shù)調(diào)用時,函數(shù)返回的結(jié)果類似于在集合上調(diào)用 toString() :各元素的字符串表示形 式以空格分隔而成的 String 土砂。
要構(gòu)建自定義字符串表示形式州既,可以在函數(shù)參數(shù) separator谜洽、prefix 與 postfix中指定其參數(shù)。 結(jié)果字符串將以 prefix 開頭吴叶,以 postfix 結(jié)尾阐虚。除最后一個元素外,separator 將位于每個元素 之后蚌卤。
val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2)) println(numberSets.flatten())
val containers = listOf( StringContainer(listOf("one", "two", "three")), StringContainer(listOf("four", "five", "six")), StringContainer(listOf("seven", "eight"))
)
println(containers.flatMap { it.values })
val numbers = listOf("one", "two", "three", "four") println(numbers)
println(numbers.joinToString())
val listString = StringBuffer("The list of numbers: ") numbers.joinTo(listString)
println(listString)
val numbers = listOf("one", "two", "three", "four") println(numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end"))
對于較大的集合实束,可能需要指定 limit ?將包含在結(jié)果中元素的數(shù)量。如果集合大小超出 limit逊彭, 所有其他元素將被 truncated 參數(shù)的單個值替換咸灿。
最后,要自定義元素本身的表示形式侮叮,請?zhí)峁?transform 函數(shù)避矢。
val numbers = (1..100).toList() println(numbers.joinToString(limit = 10, truncated = "<...>"))
val numbers = listOf("one", "two", "three", "four") println(numbers.joinToString { "Element: ${it.toUpperCase()}"})
過濾
過濾是最常用的集合處理任務(wù)之一。在Kotlin中签赃,過濾條件由 謂詞 定義?接受一個集合元素并且返回 布爾值的 lambda 表達式:true 說明給定元素與謂詞匹配谷异,false 則表示不匹配。
標(biāo)準(zhǔn)庫包含了一組讓你能夠通過單個調(diào)用就可以過濾集合的擴展函數(shù)锦聊。這些函數(shù)不會改變原始集合歹嘹,因 此它們既可用于可變集合也可用于只讀集合。為了操作過濾結(jié)果孔庭,應(yīng)該在過濾后將其賦值給變量或鏈接 其他函數(shù)尺上。
按謂詞過濾
基本的過濾函數(shù)是 filter()。當(dāng)使用一個謂詞來調(diào)用時圆到,filter() 返回與其匹配的集合元素怎抛。對于 List 和 Set ,過濾結(jié)果都是一個 List 芽淡,對 Map 來說結(jié)果還是一個 Map 马绝。
filter() 中的謂詞只能檢查元素的值。如果想在過濾中使用元素在集合中的位置挣菲,應(yīng)該使用 filterIndexed()富稻。它接受一個帶有兩個參數(shù)的謂詞:元素的索引和元素的值。
如果想使用否定條件來過濾集合白胀,請使用 filterNot()椭赋。它返回一個讓謂詞產(chǎn)生 false 的元素列表。
還有一些函數(shù)能夠通過過濾給定類型的元素來縮小元素的類型:
— filterIsInstance() 返回給定類型的集合元素或杠。在一個 List<Any> 上被調(diào)用 時哪怔,filterIsInstance<T>() 返回一個 List<T>,從而讓你能夠在集合元素上調(diào)用 T 類型的 函數(shù)。
— filterNotNull() 返回所有的非空元素认境。在一個 List<T?> 上被調(diào)用時胚委,filterNotNull() 返回一個 List<T: Any>,從而讓你能夠?qū)⑺性匾暈榉强諏ο蟆?/p>
val numbers = listOf("one", "two", "three", "four") val longerThan3 = numbers.filter { it.length > 3 } println(longerThan3)
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10} println(filteredMap)
val numbers = listOf("one", "two", "three", "four")
val filteredIdx = numbers.filterIndexed { index, s -> (index != 0) && (s.length < 5) }
val filteredNot = numbers.filterNot { it.length <= 3 }
println(filteredIdx) println(filteredNot)
val numbers = listOf(null, 1, "two", 3.0, "four") println("All String elements in upper case:") numbers.filterIsInstance<String>().forEach {
println(it.toUpperCase()) }
val numbers = listOf(null, "one", "two", null) numbers.filterNotNull().forEach {
println(it.length) // 對可空的 String 來說?度不可用 }
劃分
另一個過濾函數(shù) ? partition() ? 通過一個謂詞過濾集合并且將不匹配的元素存放在一個單獨的列 表中元暴。因此篷扩,你得到一個 List 的 Pair 作為返回值:第一個列表包含與謂詞匹配的元素并且第二個列 表包含原始集合中的所有其他元素。
檢驗謂詞
最后茉盏,有些函數(shù)只是針對集合元素簡單地檢測一個謂詞:
— 如果至少有一個元素匹配給定謂詞鉴未,那么 any() 返回 true 。
— 如果沒有元素與給定謂詞匹配鸠姨,那么 none() 返回 true 铜秆。
— 如果所有元素都匹配給定謂詞,那么 all() 返回 true 讶迁。注意连茧,在一個空集合上使用任何有效的謂 詞去調(diào)用 all() 都會返回 true 。這種行為在邏輯上被稱為 vacuous truth巍糯。
any() 和 none() 也可以不帶謂詞使用:在這種情況下它們只是用來檢查集合是否為空啸驯。如果集合 中有元素,any() 返回 true祟峦,否則返回 false;none() 則相反罚斗。
val numbers = listOf("one", "two", "three", "four")
val (match, rest) = numbers.partition { it.length > 3 }
println(match)
println(rest)
val numbers = listOf("one", "two", "three", "four")
println(numbers.any { it.endsWith("e") }) println(numbers.none { it.endsWith("a") }) println(numbers.all { it.endsWith("e") })
println(emptyList<Int>().all { it > 5 }) // vacuous truth
val numbers = listOf("one", "two", "three", "four") val empty = emptyList<String>()
println(numbers.any()) println(empty.any())
println(numbers.none()) println(empty.none())
plus 與 minus 操作符
在 Kotlin 中夭苗,為集合定義了 plus ( + ) 和 minus ( - ) 操作符怠李。它們把一個集合作為第一個操作數(shù);第二
個操作數(shù)可以是一個元素或者是另一個集合。返回值是一個新的只讀集合: — plus 的結(jié)果包含原始集合 和 第二個操作數(shù)中的元素相恃。
— minus 的結(jié)果包含原始集合中的元素厌衙,但第二個操作數(shù)中的元素 除外距淫。如果第二個操作數(shù)是一個 元素,那么 minus 移除其在原始集合中的 第一次 出現(xiàn);如果是一個集合婶希,那么移除其元素在原始集 合中的 所有 出現(xiàn)榕暇。
有 關(guān) m a p 的 p l u s 和 m i n u s 操 作 符 的 詳 細 信 息 ,請 參 ? M a p 相 關(guān) 操 作 喻杈。也 為 集 合 定 義 了 廣 義 賦 值 操作符 plusAssign ( += ) 和 minusAssign ( -= )拐揭。然而,對于只讀集合奕塑,它們實際上使用 plus 或 者 minus 操作符并嘗試將結(jié)果賦值給同一變量。因此家肯,它們僅在由 var 聲明的只讀集合中可用龄砰。對 于可變集合,如果它是一個 val,那么它們會修改集合换棚。更多詳細信息請參?集合寫操作式镐。
val numbers = listOf("one", "two", "three", "four")
val plusList = numbers + "five"
val minusList = numbers - listOf("three", "four") println(plusList)
println(minusList)
分組
Kotlin 標(biāo)準(zhǔn)庫提供用于對集合元素進行分組的擴展函數(shù)」淘椋基本函數(shù) groupBy() 使用一個 lambda 函 數(shù)并返回一個 Map 娘汞。在此 Map 中,每個鍵都是 lambda 結(jié)果夕玩,而對應(yīng)的值是返回此結(jié)果的元素 List 你弦。 例如,可以使用此函數(shù)將 String 列表按首字母分組燎孟。
還可以使用第二個 lambda 參數(shù)(值轉(zhuǎn)換函數(shù))調(diào)用 groupBy() 禽作。在帶有兩個 lambda 的 groupBy() 結(jié)果 Map 中,由 keySelector 函數(shù)生成的鍵映射到值轉(zhuǎn)換函數(shù)的結(jié)果揩页,而不是原始元
素旷偿。
如果要對元素進行分組,然后一次將操作應(yīng)用于所有分組爆侣,請使用 groupingBy() 函數(shù)萍程。它返回一個 Grouping 類型的實例。通過 Grouping 實例兔仰,可以以一種惰性的方式將操作應(yīng)用于所有組:這些分 組實際上是剛好在執(zhí)行操作前構(gòu)建的茫负。
即,Grouping 支持以下操作:
— eachCount() 計算每個組中的元素斋陪。
— fold() 與 reduce() 對每個組分別執(zhí)行 fold 與 reduce 操作朽褪,作為一個單獨的集合并返回結(jié)果。
— aggregate() 隨后將給定操作應(yīng)用于每個組中的所有元素并返回結(jié)果无虚。這是對 Grouping 執(zhí)行 任何操作的通用方法缔赠。當(dāng)折疊或縮小不夠時,可使用它來實現(xiàn)自定義操作友题。
val numbers = listOf("one", "two", "three", "four", "five")
println(numbers.groupBy { it.first().toUpperCase() })
println(numbers.groupBy(keySelector = { it.first() }, valueTransform = { it.toUpperCase() }))
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.groupingBy { it.first() }.eachCount())
取集合的一部分
Kotlin 標(biāo)準(zhǔn)庫包含用于取集合的一部分的擴展函數(shù)嗤堰。這些函數(shù)提供了多種方法來選擇結(jié)果集合的元素: 顯式列出其位置、指定結(jié)果大小等度宦。
Slice
slice() 返回具有給定索引的集合元素列表踢匣。索引既可以是作為區(qū)間傳入的也可以是作為整數(shù)值的集 合傳入的。
Take 與 drop
要從頭開始獲取指定數(shù)量的元素戈抄,請使用 take() 函數(shù)离唬。要從尾開始獲取指定數(shù)量的元素,請使用 takeLast()划鸽。當(dāng)調(diào)用的數(shù)字大于集合的大小時输莺,兩個函數(shù)都將返回整個集合戚哎。
要從頭或從尾去除給定數(shù)量的元素,請調(diào)用 drop() 或 dropLast() 函數(shù)嫂用。
還可以使用謂詞來定義要獲取或去除的元素的數(shù)量型凳。有四個與上述功能相似的函數(shù):
— takeWhile() 是帶有謂詞的 take() :它將不停獲取元素直到排除與謂詞匹配的首個元素。如果 首個集合元素與謂詞匹配嘱函,則結(jié)果為空甘畅。
— takeLastWhile() 與 takeLast() 類似:它從集合末尾獲取與謂詞匹配的元素區(qū)間。區(qū)間的首 個元素是與謂詞不匹配的最后一個元素右邊的元素往弓。如果最后一個集合元素與謂詞匹配疏唾,則結(jié)果為
空。
— dropWhile() 與具有相同謂詞的 takeWhile() 相反:它將首個與謂詞不匹配的元素返回到末
尾亮航。
— dropLastWhile() 與具有相同謂詞的 takeLastWhile() 相反:它返回從開頭到最后一個與謂
詞不匹配的元素荸实。
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.slice(1..3))
println(numbers.slice(0..4 step 2))
println(numbers.slice(setOf(3, 5, 0)))
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.take(3))
println(numbers.takeLast(3))
println(numbers.drop(1))
println(numbers.dropLast(5))
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.takeWhile { !it.startsWith('f') }) println(numbers.takeLastWhile { it != "three" }) println(numbers.dropWhile { it.length == 3 }) println(numbers.dropLastWhile { it.contains('i') })
Chunked
要將集合分解為給定大小的“塊”,請使用 chunked() 函數(shù)缴淋。chunked() 采用一個參數(shù)(塊的大小)准给, 并返回一個 List 其中包含給定大小的 List 。第一個塊從第一個元素開始并包含 size 元素重抖,第二 個塊包含下一個 size 元素露氮,依此類推。最后一個塊的大小可能較小钟沛。
還可以立即對返回的塊應(yīng)用轉(zhuǎn)換畔规。為此,請在調(diào)用 chunked() 時將轉(zhuǎn)換作為 lambda 函數(shù)提供恨统。 lambda參數(shù)是集合的一塊叁扫。當(dāng)通過轉(zhuǎn)換調(diào)用 chunked() 時,這些塊是臨時的 List畜埋,應(yīng)立即在該 lambda 中使用莫绣。
Windowed
可以檢索給定大小的集合元素中所有可能區(qū)間。獲取它們的函數(shù)稱為 windowed():它返回一個元素 區(qū)間列表悠鞍,比如通過給定大小的滑動窗口查看集合对室,則會看到該區(qū)間。與 chunked() 不 同咖祭,windowed() 返回從每個集合元素開始的元素區(qū)間(窗口)掩宜。所有窗口都作為單個 List 的元素返 回。
windowed() 通過可選參數(shù)提供更大的靈活性:
— step 定義兩個相鄰窗口的第一個元素之間的距離么翰。默認情況下牺汤,該值為 1,因此結(jié)果包含從所有元 素開始的窗口浩嫌。如果將 step 增加到 2慧瘤,將只收到以奇數(shù)元素開頭的窗口:第一個戴已、第三個等。
— partialWindows包含從集合末尾的元素開始的較小的窗口锅减。例如,如果請求三個元素的窗口伐坏,就 不能為最后兩個元素構(gòu)建它們怔匣。在本例中,啟用 partialWindows 將包括兩個大小為2與1的列表桦沉。
最后每瞒,可以立即對返回的區(qū)間應(yīng)用轉(zhuǎn)換。為此纯露,在調(diào)用 windowed() 時將轉(zhuǎn)換作為 lambda 函數(shù)提供剿骨。
要 構(gòu) 建 兩 個 元 素 的 窗 口 ,有 一 個 單 獨 的 函 數(shù) ? z i p W i t h N e x t ( ) 埠褪。它 創(chuàng) 建 接 收 器 集 合 的 相 鄰 元 素 對 浓利。 請注意,zipWithNext() 不會將集合分成幾對;它為 每個 元素創(chuàng)建除最后一個元素外的對钞速,因此它 在 [1, 2, 3, 4] 上的結(jié)果為 [[1, 2], [2, 3], [3, 4]] 贷掖,而不是 [[1, 2 ],[3, 4]] 渴语。
zipWithNext() 也可以通過轉(zhuǎn)換函數(shù)來調(diào)用;它應(yīng)該以接收者集合的兩個元素作為參數(shù)苹威。
val numbers = (0..13).toList() println(numbers.chunked(3))
val numbers = (0..13).toList()
println(numbers.chunked(3) { it.sum() }) // `it` 為原始集合的一個塊
val numbers = listOf("one", "two", "three", "four", "five") println(numbers.windowed(3))
val numbers = (1..10).toList()
println(numbers.windowed(3, step = 2, partialWindows = true)) println(numbers.windowed(3) { it.sum() })
val numbers = listOf("one", "two", "three", "four", "five") println(numbers.zipWithNext())
println(numbers.zipWithNext() { s1, s2 -> s1.length > s2.length})
取單個元素
Kotlin 集合提供了一套從集合中檢索單個元素的函數(shù)。此?面描述的函數(shù)適用于 list 和 set驾凶。
正如 list 的定義所言牙甫,list 是有序集合。因此调违,list 中的每個元素都有其位置可供你引用窟哺。除了此?面上 描述的函數(shù)外,list 還提供了更廣泛的一套方法去按索引檢索和搜索元素翰萨。有關(guān)更多詳細信息脏答,請參? List 相關(guān)操作。
反過來亩鬼,從定義來看殖告,set 并不是有序集合。但是雳锋,Kotlin 中的 Set 按某些順序存儲元素黄绩。這些可以是插 入順序(在 LinkedHashSet 中)、自然排序順序(在 SortedSet 中)或者其他順序玷过。一組元素的順序
也可以是未知的爽丹。在這種情況下筑煮,元素仍會以某種順序排序,因此粤蝎,依賴元素位置的函數(shù)仍會返回其結(jié) 果真仲。但是,除非調(diào)用者知道所使用的 Set 的具體實現(xiàn)初澎,否則這些結(jié)果對于調(diào)用者是不可預(yù)測的秸应。
按位置取
為了檢索特定位置的元素,有一個函數(shù) elementAt()碑宴。用一個整數(shù)作為參數(shù)來調(diào)用它软啼,你會得到給定 位置的集合元素。第一個元素的位置是 0 延柠,最后一個元素的位置是 (size - 1) 祸挪。
elementAt() 對于不提供索引訪問或非靜態(tài)已知提供索引訪問的集合很有用。在使用 List 的情況 下贞间,使用索引訪問操作符(get() 或 [])更為習(xí)慣贿条。
還有一些有用的別名來檢索集合的第一個和最后一個元素:first() 和 last()。
為了避免在檢索位置不存在的元素時出現(xiàn)異常榜跌,請使用 elementAt() 的安全變體: — 當(dāng)指定位置超出集合范圍時闪唆,elementAtOrNull()返回null。
— elementAtOrElse() 還接受一個 lambda 表達式钓葫,該表達式能將一個 Int 參數(shù)映射為一個集合 元素類型的實例悄蕾。當(dāng)使用一個越界位置來調(diào)用時,elementAtOrElse() 返回對給定值調(diào)用該 lambda 表達式的結(jié)果础浮。
按條件取
val numbers = linkedSetOf("one", "two", "three", "four", "five") println(numbers.elementAt(3))
val numbersSortedSet = sortedSetOf("one", "two", "three", "four") println(numbersSortedSet.elementAt(0)) // 元素以升序存儲
val numbers = listOf("one", "two", "three", "four", "five") println(numbers.first())
println(numbers.last())
val numbers = listOf("one", "two", "three", "four", "five") println(numbers.elementAtOrNull(5))
println(numbers.elementAtOrElse(5) { index -> "The value for index $index is undefined"})
函數(shù) first() 和 last() 還可以讓你在集合中搜索與給定謂詞匹配的元素帆调。當(dāng)你使用測試集合元素 的謂詞調(diào)用 first() 時,你會得到對其調(diào)用謂詞產(chǎn)生 true 的第一個元素豆同。反過來番刊,帶有一個謂詞的
last() 返回與其匹配的最后一個元素。
如果沒有元素與謂詞匹配影锈,兩個函數(shù)都會拋異常芹务。為了避免它們,請改用 firstOrNull() 和 lastOrNull():如果找不到匹配的元素鸭廷,它們將返回 null 枣抱。
或者,如果別名更適合你的情況辆床,那么可以使用別名: — 使用 find() 代替 firstOrNull()
— 使用 findLast() 代替 lastOrNull()
隨機取元素
如果需要檢索集合的一個隨機元素佳晶,那么請調(diào)用 random() 函數(shù)。你可以不帶參數(shù)或者使用一個 Random 對象作為隨機源來調(diào)用它讼载。
檢測存在與否
如需檢查集合中某個元素的存在轿秧,可以使用 contains() 函數(shù)中跌。如果存在一個集合元素等于 ( equals() )函數(shù)參數(shù),那么它返回 true 菇篡。你可以使用 in 關(guān)鍵字以操作符的形式調(diào)用
contains() 漩符。
如需一次檢查多個實例的存在,可以使用這些實例的集合作為參數(shù)調(diào)用 containsAll()驱还。
此外陨仅,你可以通過調(diào)用 isEmpty() 和 isNotEmpty() 來檢查集合中是否包含任何元素。
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.first { it.length > 3 })
println(numbers.last { it.startsWith("f") })
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.firstOrNull { it.length > 6 })
val numbers = listOf(1, 2, 3, 4) println(numbers.find { it % 2 == 0 }) println(numbers.findLast { it % 2 == 0 })
val numbers = listOf(1, 2, 3, 4) println(numbers.random())
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.contains("four"))
println("zero" in numbers)
println(numbers.containsAll(listOf("four", "two"))) println(numbers.containsAll(listOf("one", "zero")))
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.isEmpty())
println(numbers.isNotEmpty())
val empty = emptyList<String>() println(empty.isEmpty()) println(empty.isNotEmpty())
集合排序
元素的順序是某些集合類型的一個重要方面铝侵。例如,如果擁有相同元素的兩個列表的元素順序不同触徐,那 么這兩個列表也不相等咪鲜。
在 Kotlin 中,可以通過多種方式定義對象的順序撞鹉。
首先疟丙,有 自然 順序。它是為 Comparable 接口的繼承者定義的鸟雏。當(dāng)沒有指定其他順序時享郊,使用自然順序 為它們排序。
大多數(shù)內(nèi)置類型是可比較的:
— 數(shù)值類型使用傳統(tǒng)的數(shù)值順序:1 大于 0 ;-3.4f 大于 -5f 孝鹊,以此類推炊琉。
— Char 和 String 使用字典順序:b 大于 a ;world 大于 hello 。
如需為用戶定義的類型定義一個自然順序又活,可以讓這個類型繼承 Comparable 苔咪。這需要實現(xiàn) compareTo() 函數(shù)。compareTo() 必須將另一個具有相同類型的對象作為參數(shù)并返回一個整數(shù)值
來顯示哪個對象更大:
— 正值表明接收者對象更大柳骄。 — 負值表明它小于參數(shù)团赏。
— 0說明對象相等。
下面是一個類耐薯,可用于排序由主版本號和次版本號兩部分組成的版本舔清。
class Version(val major: Int, val minor: Int): Comparable<Version> { override fun compareTo(other: Version): Int {
if (this.major != other.major) { return this.major - other.major
} else if (this.minor != other.minor) { return this.minor - other.minor
} else return 0 }
}
fun main() {
println(Version(1, 2) > Version(1, 3)) println(Version(2, 0) > Version(1, 5))
}
自定義 順序
讓你可以按自己喜歡的方式對任何類型的實例進行排序。特別是曲初,你可以為不可比較類型 定義順序体谒,或者為可比較類型定義非自然順序。如需為類型定義自定義順序复斥,可以為其創(chuàng)建一個 Comparator营密。Comparator 包含 compare() 函數(shù):它接受一個類的兩個實例并返回它們之間比較
的整數(shù)結(jié)果。如上所述目锭,對結(jié)果的解釋與 compareTo() 的結(jié)果相同评汰。
val lengthComparator = Comparator { str1: String, str2: String -> str1.length - str2.length }
println(listOf("aaa", "bb", "c").sortedWith(lengthComparator))
有了 lengthComparator纷捞,你可以按照字符串的?度而不是默認的字典順序來排列字符串。
定義一個 Comparator 的一種比較簡短的方式是標(biāo)準(zhǔn)庫中的 compareBy() 函數(shù)被去。compareBy() 接受一個 lambda 表達式主儡,該表達式從一個實例產(chǎn)生一個 Comparable 值,并將自定義順序定義為生 成值的自然順序惨缆。使用 compareBy()糜值,上面示例中的?度比較器如下所示:
Kotlin 集合包提供了用于按照自然順序、自定義順序甚至隨機順序?qū)吓判虻暮瘮?shù)坯墨。在此?面上寂汇,我 們將介紹適用于只讀集合的排序函數(shù)。這些函數(shù)將它們的結(jié)果作為一個新集合返回捣染,集合里包含了按照 請 求 順 序 排 序 的 來 自 原 始 集 合 的 元 素 骄瓣。如 果 想 學(xué) 習(xí) 就 地 對 可 變 集 合 排 序 的 函 數(shù) ,請 參 ? L i s t 相 關(guān) 操 作 耍攘。
自然順序
基本的函數(shù) sorted() 和 sortedDescending() 返回集合的元素榕栏,這些元素按照其自然順序升序和 降序排序。這些函數(shù)適用于 Comparable 元素的集合蕾各。
自定義順序
為了按照自定義順序排序或者對不可比較對象排序扒磁,可以使用函數(shù) sortedBy() 和 sortedByDescending()。它們接受一個將集合元素映射為 Comparable 值的選擇器函數(shù)式曲,并以該 值的自然順序?qū)吓判颉?如需為集合排序定義自定義順序妨托,可以提供自己的 Comparator 。為此检访,調(diào)用傳入 Comparator 的 sortedWith() 函數(shù)始鱼。使用此函數(shù),按照字符串?度排序如下所示:
###倒序
你可以使用 reversed() 函數(shù)以相反的順序檢索集合脆贵。
tln(listOf("aaa", "bb", "c").sortedWith(compareBy { it.length }))
val numbers = listOf("one", "two", "three", "four")
println("Sorted ascending: ${numbers.sorted()}") println("Sorted descending: ${numbers.sortedDescending()}")
val numbers = listOf("one", "two", "three", "four")
val sortedNumbers = numbers.sortedBy { it.length } println("Sorted by length ascending: $sortedNumbers")
val sortedByLast = numbers.sortedByDescending { it.last() } println("Sorted by the last letter descending: $sortedByLast")
val numbers = listOf("one", "two", "three", "four")
println("Sorted by length ascending: ${numbers.sortedWith(compareBy { it.length })}")
val numbers = listOf("one", "two", "three", "four") println(numbers.reversed())
reversed() 返回帶有元素副本的新集合医清。因此,如果你之后改變了原始集合卖氨,這并不會影響先前獲 得的 reversed() 的結(jié)果会烙。
另 一 個 反 向 函 數(shù) ? a s R e v e r s e d ( ) ? 返 回 相 同 集 合 實 例 的 一 個 反 向 視 圖 ,因 此 筒捺,如 果 原 始 列 表 不 會 發(fā)生變化柏腻,那么它會比 reversed() 更輕量,更合適系吭。
如果原始列表是可變的五嫂,那么其所有更改都會反映在其反向視圖中,反之亦然。
但是沃缘,如果列表的可變性未知或者源根本不是一個列表躯枢,那么 reversed() 更合適,因為其結(jié)果是一 個未來不會更改的副本槐臀。
隨機順序
最后锄蹂,shuffled() 函數(shù)返回一個包含了以隨機順序排序的集合元素的新的 List 。你可以不帶參數(shù) 或者使用 Random 對象來調(diào)用它水慨。
val numbers = listOf("one", "two", "three", "four") val reversedNumbers = numbers.asReversed() println(reversedNumbers)
val numbers = mutableListOf("one", "two", "three", "four") val reversedNumbers = numbers.asReversed() println(reversedNumbers)
numbers.add("five")
println(reversedNumbers)
val numbers = listOf("one", "two", "three", "four") println(numbers.shuffled())
集合聚合操作
Kotlin 集合包含用于常用的 聚合操作(基于集合內(nèi)容返回單個值的操作)的函數(shù) 得糜。其中大多數(shù)是眾所 周知的,并且其工作方式與在其他語言中相同晰洒。
— min() 與 max() 分別返回最小和最大的元素;
— average() 返回數(shù)字集合中元素的平均值;
— sum() 返回數(shù)字集合中元素的總和;
— count() 返回集合中元素的數(shù)量;
還有一些通過某些選擇器函數(shù)或自定義 Comparator 來檢索最小和最大元素的函數(shù)朝抖。
— maxBy()/minBy() 接受一個選擇器函數(shù)并返回使選擇器返回最大或最小值的元素。
— maxWith()/minWith() 接受一個 Comparator 對象并且根據(jù)此 Comparator 對象返回最大 或最小元素谍珊。
此外槽棍,有一些高級的求和函數(shù),它們接受一個函數(shù)并返回對所有元素調(diào)用此函數(shù)的返回值的總和: — sumBy() 使用對集合元素調(diào)用返回 Int 值的函數(shù)抬驴。
— sumByDouble() 與返回 Double 的函數(shù)一起使用。
##Fold 與 reduce
對于更特定的情況缆巧,有函數(shù) reduce() 和 fold()布持,它們依次將所提供的操作應(yīng)用于集合元素并返回累 積的結(jié)果。操作有兩個參數(shù):先前的累積值和集合元素陕悬。
val numbers = listOf(6, 42, 10, 4)
println("Count: ${numbers.count()}") println("Max: ${numbers.max()}") println("Min: ${numbers.min()}") println("Average: ${numbers.average()}") println("Sum: ${numbers.sum()}")
val numbers = listOf(5, 42, 10, 4)
val min3Remainder = numbers.minBy { it % 3 } println(min3Remainder)
val strings = listOf("one", "two", "three", "four")
val longestString = strings.maxWith(compareBy { it.length }) println(longestString)
val numbers = listOf(5, 42, 10, 4) println(numbers.sumBy { it * 2 }) println(numbers.sumByDouble { it.toDouble() / 2 })
這兩個函數(shù)的區(qū)別在于:fold() 接受一個初始值并將其用作第一步的累積值题暖,而 reduce() 的第一 步則將第一個和第二個元素作為第一步的操作參數(shù)。
val numbers = listOf(5, 2, 10, 4)
val sum = numbers.reduce { sum, element -> sum + element } println(sum)
val sumDoubled = numbers.fold(0) { sum, element -> sum + element * 2 } println(sumDoubled)
//val sumDoubledReduce = numbers.reduce { sum, element -> sum + element * 2 } //錯誤:第一個 元素在結(jié)果中沒有加倍
//println(sumDoubledReduce)
上面的實例展示了區(qū)別:fold() 用于計算加倍的元素之和捉超。如果將相同的函數(shù)傳給 reduce()胧卤,那 么它會返回另一個結(jié)果,因為在第一步中它將列表的第一個和第二個元素作為參數(shù)拼岳,所以第一個元素不
會被加倍枝誊。
如需將函數(shù)以相反的順序應(yīng)用于元素,可以使用函數(shù) reduceRight() 和 foldRight() 它們的工作
方式類似于 fold() 和 reduce()惜纸,但從最后一個元素開始叶撒,然后再繼續(xù)到前一個元素。記住耐版,在使用
foldRight 或 reduceRight 時祠够,操作參數(shù)會更改其順序:第一個參數(shù)變?yōu)樵兀缓蟮诙€參數(shù)變?yōu)槔鄯e 值粪牲。
你還可以使用將元素索引作為參數(shù)的操作古瓤。為此,使用函數(shù) reduceIndexed() 和 foldIndexed() 傳遞元素索引作為操作的第一個參數(shù)。
最后落君,還有將這些操作從右到左應(yīng)用于集合元素的函數(shù)?reduceRightIndexed() 與 foldRightIndexed()穿香。
val numbers = listOf(5, 2, 10, 4)
val sumDoubledRight = numbers.foldRight(0) { element, sum -> sum + element * 2 } println(sumDoubledRight)
val numbers = listOf(5, 2, 10, 4)
val sumEven = numbers.foldIndexed(0) { idx, sum, element -> if (idx % 2 == 0) sum + element else sum }
println(sumEven)
val sumEvenRight = numbers.foldRightIndexed(0) { idx, element, sum -> if (idx % 2 == 0) sum + element else sum }
println(sumEvenRight)
集合寫操作
可變集合支持更改集合內(nèi)容的操作,例如添加或刪除元素叽奥。在此?面上扔水,我們將描述實現(xiàn) MutableCollection 的所有寫操作。有關(guān) List 與 Map 可用的更多特定操作朝氓,請分別參? List 相
關(guān)操作與 Map 相關(guān)操作魔市。 添加元素
要將單個元素添加到列表或集合,請使用 add() 函數(shù)赵哲。指定的對象將添加到集合的末尾待德。
addAll() 將參數(shù)對象的每個元素添加到列表或集合中。參數(shù)可以是 Iterable 枫夺、Sequence 或 Array 将宪。接收者的類型和參數(shù)可能不同,例如橡庞,你可以將所有內(nèi)容從 Set 添加到 List 较坛。
當(dāng)在列表上調(diào)用時,addAll() 會按照在參數(shù)中出現(xiàn)的順序添加各個新元素扒最。你也可以調(diào)用 addAll() 時指定一個元素位置作為第一參數(shù)丑勤。參數(shù)集合的第一個元素會被插入到這個位置。其他元
素將跟隨在它后面吧趣,將接收者元素移到末尾法竞。
你還可以使用 plus 運算符 - plusAssign ( += ) 添加元素。當(dāng)應(yīng)用于可變集合時强挫,+= 將第二個操作 數(shù)(一個元素或另一個集合)追加到集合的末尾岔霸。
##刪除元素
若要從可變集合中移除元素,請使用 remove() 函數(shù)俯渤。remove() 接受元素值呆细,并刪除該值的一個匹 配項。
要一次刪除多個元素八匠,有以下函數(shù):
— removeAll() 移除參數(shù)集合中存在的所有元素侦鹏。或者臀叙,你可以用謂詞作為參數(shù)來調(diào)用它;在這種情
val numbers = mutableListOf(1, 2, 3, 4) numbers.add(5)
println(numbers)
val numbers = mutableListOf(1, 2, 5, 6) numbers.addAll(arrayOf(7, 8)) println(numbers)
numbers.addAll(2, setOf(3, 4)) println(numbers)
val numbers = mutableListOf("one", "two") numbers += "three"
println(numbers)
numbers += listOf("four", "five") println(numbers)
val numbers = mutableListOf(1, 2, 3, 4, 3) numbers.remove(3) // 刪除了第一個 `3` println(numbers)
numbers.remove(5) // 什么都沒刪除 println(numbers)
況下略水,函數(shù)移除謂詞產(chǎn)生 true 的所有元素。
— r e t a i n A l l ( ) 與 r e m o v e A l l ( ) 相 反 :它 移 除 除 參 數(shù) 集 合 中 的 元 素 之 外 的 所 有 元 素 劝萤。當(dāng) 與 謂 詞 一 起使用時渊涝,它只留下與之匹配的元素。
— clear() 從列表中移除所有元素并將其置空。
val numbers = mutableListOf(1, 2, 3, 4) println(numbers)
numbers.retainAll { it >= 3 } println(numbers)
numbers.clear()
println(numbers)
val numbersSet = mutableSetOf("one", "two", "three", "four") numbersSet.removeAll(setOf("one", "two")) println(numbersSet)
從集合中移除元素的另一種方法是使用 minusAssign ( -= ) ?原地修改版的 minus 操作符跨释。
minus 操作符胸私。第二個參數(shù)可以是元素類型的單個實例或另一個集合。右邊是單個元素時鳖谈,-= 會移除
它的第一個匹配項岁疼。反過來,如果它是一個集合,那么它的所有元素的每次出現(xiàn)都會刪除。例如担败,如果列 表包含重復(fù)的元素,它們將被同時刪除暖侨。第二個操作數(shù)可以包含集合中不存在的元素。這些元素不會影 響操作的執(zhí)行崇渗。
更新元素
list 和 map 還提供更新元素的操作字逗。它們在 List 相關(guān)操作與 Map 相關(guān)操作中有所描述。對于 set 來 說宅广,更新沒有意義葫掉,因為它實際上是移除一個元素并添加另一個元素。
val numbers = mutableListOf("one", "two", "three", "three", "four") numbers -= "three"
println(numbers)
numbers -= listOf("four", "five")
//numbers -= listOf("four") // 與上述相同 println(numbers)
List 相關(guān)操作
List 是 Kotlin 標(biāo)準(zhǔn)庫中最受歡迎的集合類型跟狱。對列表元素的索引訪問為 List 提供了一組強大的操作挖息。 按索引取元素
List 支持按索引取元素的所有常用操作:elementAt() 、first() 兽肤、last() 與取單個元素中列 出的其他操作。List 的特點是能通過索引訪問特定元素绪抛,因此讀取元素的最簡單方法是按索引檢索它资铡。
這是通過 get() 函數(shù)或簡寫語法 [index] 來傳遞索引參數(shù)完成的。
如果 List ?度小于指定的索引幢码,則拋出異常笤休。另外,還有兩個函數(shù)能避免此類異常:
— getOrElse() 提供用于計算默認值的函數(shù)症副,如果集合中不存在索引店雅,則返回默認值。 — getOrNull() 返回 null 作為默認值贞铣。
###取列表的一部分
除了取集合的一部分中常用的操作闹啦,List 還提供 subList() 該函數(shù)將指定元素范圍的視圖作為列表 返回。因此辕坝,如果原始集合的元素發(fā)生變化窍奋,則它在先前創(chuàng)建的子列表中也會發(fā)生變化,反之亦然。
查找元素位置 線性查找
在任何列表中琳袄,都可以使用 indexOf() 或 lastIndexOf() 函數(shù)找到元素的位置江场。它們返回與列表 中給定參數(shù)相等的元素的第一個或最后一個位置。如果沒有這樣的元素窖逗,則兩個函數(shù)均返回 -1 址否。
還有一對函數(shù)接受謂詞并搜索與之匹配的元素:
— indexOfFirst() 返回與謂詞匹配的第一個元素的索引,如果沒有此類元素碎紊,則返回 -1 佑附。
— indexOfLast() 返回與謂詞匹配的最后一個元素的索引,如果沒有此類元素矮慕,則返回 -1 帮匾。
val numbers = listOf(1, 2, 3, 4) println(numbers.get(0)) println(numbers[0]) //numbers.get(5) println(numbers.getOrNull(5)) println(numbers.getOrElse(5, {it}))
// exception!
// null
// 5
val numbers = (0..13).toList() println(numbers.subList(3, 6))
val numbers = listOf(1, 2, 3, 4, 2, 5) println(numbers.indexOf(2)) println(numbers.lastIndexOf(2))
val numbers = mutableListOf(1, 2, 3, 4) println(numbers.indexOfFirst { it > 2}) println(numbers.indexOfLast { it % 2 == 1})
在有序列表中二分查找
還 有 另 一 種 搜 索 列 表 中 元 素 的 方 法 ? 二 分 查 找 算 法 。它 的 工 作 速 度 明 顯 快 于 其 他 內(nèi) 置 搜 索 功 能 痴鳄,但 要 求該列表按照一定的順序(自然排序或函數(shù)參數(shù)中提供的另一種排序)按升序排序過瘟斜。否則,結(jié)果是不確 定的痪寻。
要搜索已排序列表中的元素螺句,請調(diào)用 binarySearch() 函數(shù),并將該值作為參數(shù)傳遞橡类。如果存在這樣 的元素蛇尚,則函數(shù)返回其索引;否則,將返回 (-insertionPoint - 1)顾画,其中 insertionPoint 為 應(yīng)插入此元素的索引取劫,以便列表保持排序。如果有多個具有給定值的元素研侣,搜索則可以返回其任何索引谱邪。
還可以指定要搜索的索引區(qū)間:在這種情況下,該函數(shù)僅在兩個提供的索引之間搜索庶诡。
###Comparator 二分搜索
如果列表元素不是 Comparable 惦银,則應(yīng)提供一個用于二分搜索的 Comparator。該列表必須根據(jù)此
Comparator 以升序排序末誓。來看一個例子:
這是一個不可 排序 的 Product 實例列表扯俱,以及一個定義排序的 Comparator :如果 p1 的價格小于 p2 的價格,則產(chǎn)品 p1 在產(chǎn)品 p2 之前喇澡。因此迅栅,按照此順序?qū)α斜磉M行升序排序后,使用 binarySearch() 查找指定的 Product 的索引晴玖。
當(dāng)列表使用與自然排序不同的順序時(例如库继,對 String 元素不區(qū)分大小寫的順序)箩艺,自定義 Comparator 也很方便。
比較函數(shù)二分搜索
val numbers = mutableListOf("one", "two", "three", "four") numbers.sort()
println(numbers)
println(numbers.binarySearch("two")) // 3 println(numbers.binarySearch("z")) // -5 println(numbers.binarySearch("two", 0, 2)) // -3
val productList = listOf( Product("WebStorm", 49.0), Product("AppCode", 99.0), Product("DotTrace", 129.0), Product("ReSharper", 149.0))
println(productList.binarySearch(Product("AppCode", 99.0), compareBy<Product> { it.price }.thenBy { it.name }))
val colors = listOf("Blue", "green", "ORANGE", "Red", "yellow") println(colors.binarySearch("RED", String.CASE_INSENSITIVE_ORDER)) // 3
使用 比較 函數(shù)的二分搜索無需提供明確的搜索值即可查找元素宪萄。取而代之的是艺谆,它使用一個比較函數(shù) 將元素映射到 Int 值,并搜索函數(shù)返回 0 的元素拜英。該列表必須根據(jù)提供的函數(shù)以升序排序;換句話說静汤,
比較的返回值必須從一個列表元素增?到下一個列表元素。
data class Product(val name: String, val price: Double)
fun priceComparison(product: Product, price: Double) = sign(product.price - price).toInt()
fun main() {
val productList = listOf(
Product("WebStorm", 49.0), Product("AppCode", 99.0), Product("DotTrace", 129.0), Product("ReSharper", 149.0))
println(productList.binarySearch { priceComparison(it, 99.0) }) }
Comparator 與比較函數(shù)二分搜索都可以針對列表區(qū)間執(zhí)行居凶。
List 寫操作
除了集合寫操作中描述的集合修改操作之外虫给,可變列表還支持特定的寫操作。這些操作使用索引來訪問 元素以擴展列表修改功能侠碧。
添加
要將元素添加到列表中的特定位置抹估,請使用 add() 或 addAll() 并提供元素插入的位置作為附加參 數(shù)。位置之后的所有元素都將向右移動弄兜。
####更新
列表還提供了在指定位置替換元素的函數(shù)?set() 及其操作符形式 [] 药蜻。set() 不會更改其他元素 的索引。
fill() 簡單地將所有集合元素的值替換為指定值替饿。
val numbers = mutableListOf("one", "five", "six") numbers.add(1, "two")
numbers.addAll(2, listOf("three", "four")) println(numbers)
val numbers = mutableListOf("one", "five", "three") numbers[1] = "two"
println(numbers)
val numbers = mutableListOf(1, 2, 3, 4) numbers.fill(3)
println(numbers)
刪除
要從列表中刪除指定位置的元素语泽,請使用 removeAt() 函數(shù),并將位置作為參數(shù)视卢。在元素被刪除之后出 現(xiàn)的所有元素索引將減 1踱卵。
排序
在集合排序中,描述了按特定順序檢索集合元素的操作据过。對于可變列表惋砂,標(biāo)準(zhǔn)庫中提供了類似的擴展函 數(shù),這些擴展函數(shù)可以執(zhí)行相同的排序操作绳锅。將此類操作應(yīng)用于列表實例時西饵,它將更改指定實例中元素 的順序。
就地排序函數(shù)的名稱與應(yīng)用于只讀列表的函數(shù)的名稱相似榨呆,但沒有 ed/d 后綴:
val numbers = mutableListOf(1, 2, 3, 4, 3) numbers.removeAt(1)
println(numbers)
—
— —
中的更改將反映在原始列表中。以下示例展示了可變列表的排序函數(shù):
sort* 在所有排序函數(shù)的名稱中代替 sorted* :sort()庸队、sortDescending()积蜻、sortBy() 等 等。
shuffle() 代替 shuffled() 彻消。
reverse() 代替 reversed() 竿拆。
asReversed() 在可變列表上調(diào)用會返回另一個可變列表,該列表是原始列表的反向視圖宾尚。在該視圖
val numbers = mutableListOf("one", "two", "three", "four")
numbers.sort()
println("Sort into ascending: $numbers") numbers.sortDescending()
println("Sort into descending: $numbers")
numbers.sortBy { it.length }
println("Sort into ascending by length: $numbers") numbers.sortByDescending { it.last() }
println("Sort into descending by the last letter: $numbers")
numbers.sortWith(compareBy<String> { it.length }.thenBy { it }) println("Sort by Comparator: $numbers")
numbers.shuffle() println("Shuffle: $numbers")
numbers.reverse() println("Reverse: $numbers")
Set 相關(guān)操作
Kotlin 集合包中包含 set 常用操作的擴展函數(shù):查找交集丙笋、并集或差集谢澈。
要將兩個集合合并為一個(并集),可使用 union() 函數(shù)御板。也能以中綴形式使用 a union b 锥忿。注意,對 于有序集合怠肋,操作數(shù)的順序很重要:在結(jié)果集合中敬鬓,左側(cè)操作數(shù)在前。
要查找兩個集合中都存在的元素(交集)笙各,請使用 intersect() 钉答。要查找另一個集合中不存在的集合元 素(差集),請使用 subtract() 杈抢。這兩個函數(shù)也能以中綴形式調(diào)用数尿,例如,a intersect b 惶楼。
注意右蹦,List 也支持 Set 操作。但是鲫懒,對 List 進行 Set 操作的結(jié)果仍然是 Set 嫩实,因此將刪除所有重復(fù)的 元素。
val numbers = setOf("one", "two", "three") println(numbers union setOf("four", "five"))
println(setOf("four", "five") union numbers)
println(numbers intersect setOf("two", "one")) println(numbers subtract setOf("three", "four")) println(numbers subtract setOf("four", "three")) // 相同的輸出
Map 相關(guān)操作
在 map 中窥岩,鍵和值的類型都是用戶定義的甲献。對基于鍵的訪問啟用了各種特定于 map 的處理函數(shù),從鍵 獲取值到對鍵和值進行單獨過濾颂翼。在此?面上晃洒,我們提供了來自標(biāo)準(zhǔn)庫的 map 處理功能的描述。
取鍵與值
要從 Map 中檢索值朦乏,必須提供其鍵作為 get() 函數(shù)的參數(shù)球及。還支持簡寫 [key] 語法。如果找不到給 定的鍵呻疹,則返回 null 吃引。還有一個函數(shù) getValue() ,它的行為略有不同:如果在 Map 中找不到鍵刽锤,則 拋出異常镊尺。此外,還有兩個選項可以解決鍵缺失的問題:
— getOrElse() 與 list 的工作方式相同:對于不存在的鍵并思,其值由給定的 lambda 表達式返回庐氮。
— getOrDefault() 如果找不到鍵,則返回指定的默認值宋彼。
要對 map 的所有鍵或所有值執(zhí)行操作弄砍,可以從屬性 keys 和 values 中相應(yīng)地檢索它們仙畦。keys 是 Map 中所有鍵的集合,values 是 Map 中所有值的集合音婶。
###過濾
可以使用 filter() 函數(shù)來過濾 map 或其他集合慨畸。對 map 使用 filter() 函數(shù)時,Pair 將作為 參數(shù)的謂詞傳遞給它桃熄。它將使用謂詞同時過濾其中的鍵和值先口。
還有兩種用于過濾 map 的特定函數(shù):按鍵或按值。這兩種方式瞳收,都有對應(yīng)的函數(shù):filterKeys() 和 filterValues() 碉京。兩者都將返回一個新 Map ,其中包含與給定謂詞相匹配的條目螟深。
filterKeys() 的謂詞僅檢查元素鍵谐宙,filterValues() 的謂詞僅檢查值。
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap.get("one"))
println(numbersMap["one"]) println(numbersMap.getOrDefault("four", 10)) println(numbersMap["five"]) // null //numbersMap.getValue("six") // exception!
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap.keys)
println(numbersMap.values)
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10} println(filteredMap)
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11) val filteredKeysMap = numbersMap.filterKeys { it.endsWith("1") }
val filteredValuesMap = numbersMap.filterValues { it < 10 }
println(filteredKeysMap) println(filteredValuesMap)
plus 與 minus 操作
由于需要訪問元素的鍵界弧,plus( + )與 minus( - )運算符對 map 的作用與其他集合不同凡蜻。plus 返回 包 含 兩 個 操 作 數(shù) 元 素 的 M a p :左 側(cè) 的 M a p 與 右 側(cè) 的 P a i r 或 另 一 個 M a p 。當(dāng) 右 側(cè) 操 作 數(shù) 中 有 左 側(cè)
Map 中已存在的鍵時垢箕,該條目將使用右側(cè)的值划栓。
minus 將根據(jù)左側(cè) Map 條目創(chuàng)建一個新 Map ,右側(cè)操作數(shù)帶有鍵的條目將被剔除条获。因此忠荞,右側(cè)操作 數(shù)可以是單個鍵或鍵的集合:list 、set 等帅掘。
關(guān)于在可變 Map 中使用 plusAssign( += )與 minusAssign( -= )運算符的詳細信息委煤,請參? Map 寫操作 。
Map 寫操作
>Mutable Map(可變 Map )提供特定的 Map 寫操作修档。這些操作使你可以使用鍵來訪問或更改 Map 值碧绞。 Map 寫操作的一些規(guī)則:
— 值可以更新。反過來吱窝,鍵也永遠不會改變:添加條目后讥邻,鍵是不變的。 — 每個鍵都有一個與之關(guān)聯(lián)的值院峡。也可以添加和刪除整個條目兴使。
下面是對可變 Map 中可用寫操作的標(biāo)準(zhǔn)庫函數(shù)的描述。
添加與更新條目
要將新的鍵值對添加到可變 Map 撕予,請使用 put() 鲫惶。將新條目放入 LinkedHashMap(Map的默認實 現(xiàn))后蜈首,會添加該條目实抡,以便在 Map 迭代時排在最后欠母。在 Map 類中,新元素的位置由其鍵順序定義吆寨。
要一次添加多個條目赏淌,請使用 putAll() 。它的參數(shù)可以是 Map 或一組 Pair :Iterable 啄清、 Sequence 或 Array 六水。
如果給定鍵已存在于 Map 中,則 put() 與 putAll() 都將覆蓋值辣卒。因此掷贾,可以使用它們來更新 Map 條目的值。
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap + Pair("four", 4))
println(numbersMap + Pair("one", 10))
println(numbersMap + mapOf("five" to 5, "one" to 11))
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3) println(numbersMap - "one")
println(numbersMap - listOf("two", "four"))
val numbersMap = mutableMapOf("one" to 1, "two" to 2) numbersMap.put("three", 3)
println(numbersMap)
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3) numbersMap.putAll(setOf("four" to 4, "five" to 5)) println(numbersMap)
val numbersMap = mutableMapOf("one" to 1, "two" to 2)
val previousValue = numbersMap.put("one", 11)
println("value associated with 'one', before: $previousValue, after: ${numbersMap["one"]}")
println(numbersMap)
還可以使用快速操作符將新條目添加到 Map 荣茫。有兩種方式: — plusAssign( += )操作符想帅。
— [] 操作符為 put() 的別名。
使用 Map 中存在的鍵進行操作時啡莉,將覆蓋相應(yīng)條目的值港准。
刪除條目
要從可變 Map 中刪除條目,請使用 remove() 函數(shù)咧欣。調(diào)用 remove() 時浅缸,可以傳遞鍵或整個鍵值對。 如果同時指定鍵和值魄咕,則僅當(dāng)鍵值都匹配時衩椒,才會刪除此的元素。
還可以通過鍵或值從可變 Map 中刪除條目蚕礼。在 Map 的 .keys 或 .values 中調(diào)用 remove() 并 提供鍵或值來刪除條目烟具。在 .values 中調(diào)用時,remove() 僅刪除給定值匹配到的的第一個條目奠蹬。
minusAssign( -= )操作符也可用于可變 Map 朝聋。
val numbersMap = mutableMapOf("one" to 1, "two" to 2) numbersMap["three"] = 3 // 調(diào)用 numbersMap.put("three", 3) numbersMap += mapOf("four" to 4, "five" to 5) println(numbersMap)
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3) numbersMap.remove("one")
println(numbersMap)
numbersMap.remove("three", 4) //不會刪除任何條目 println(numbersMap)
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3, "threeAgain" to 3) numbersMap.keys.remove("one")
println(numbersMap)
numbersMap.values.remove(3)
println(numbersMap)
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3) numbersMap -= "two"
println(numbersMap)
numbersMap -= "five" //不會刪除任何條目 println(numbersMap)