一次性討論清楚Kotlin的map和flatMap
持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第9天,點選檢視活動詳情。
Kotlin
的官方擴充套件函式一直給我的感覺就是:簡單、好用還呈現一種“只有你想不到沒有你找不到”的態勢。
今天咱不聊多麼複雜的,就來談談 map
和 flatMap
,名字很像,到底這兩個貨有什麼區別呢?分別面對的又是什麼使用場景?
概述
首先,名字即功能,map
和 flatMap
是為實現“對映”而存在的,官網是這麼描述對映的:
The mapping transformation creates a collection from the results of a function on the elements of another collection.
意思就是:提供一個轉換函式,用以把一個集合的元素轉化生成另一個集合,而其中最為基礎的就是 map
。
其次,這兩個不是單屬於某個特定型別的擴充套件,它們寫出來就是面向所有可能需要有“對映”需求的地方的。不信來看看:
```kotlin
// 陣列擴充套件
public inline fun
// 迭代類擴充套件
public inline fun
// 對映擴充套件
public inline fun
都是泛型實現,涵蓋的型別可真不少,甚至還有“對映的對映”這樣的。像Iterable<T>
這樣的,一擴充套件,支援的型別那可多了,所有的Collections<T>
型別都是,比如List
、Set
;另外,Kotlin
的 Range 型別也沒落下。
kotlin
fun supportNeverEnough() {
arrayOf<Int>().map { }
listOf<String>().map { }
hashMapOf<Int, String>().map { }
setOf<Int>().map { }
(0 until 10).map { }
// 好了,不繼續寫了……
}
當然,flatMap
也是一樣的。
而且值得注意的是,不管是哪個map,其結果都是List
型別。
map
map
接受傳入的轉換函式,處理後,即將源轉化成一個新的List,且這個新的List的元素順序和其源是一致的。
對於“陣列”類的源(比如list, set, array等),map
為:
kotlin
fun <T, R> XXX<T>.map(transform: (T) -> R): List<R>
可以看到,map就是將T型別的集合轉化成了R型別的List。
陣列型別
```kotlin
public inline fun
public inline fun
map
內部會呼叫 Array<out T>.mapTo
方法,該方法第一個引數是 MutableCollection<in R>
的子型別,即可變集合,用來迭代儲存結果元素。
第二個引數就是轉換函式,將T型別轉為R型別。
內部 for 迴圈迭代所有元素,每個元素呼叫轉換函式,生成結果並新增至集合,最後返回。整個過程算是十分簡單了。
迭代集合型別
這是針對迭代集合 Iterable
的:
```kotlin
public inline fun
internal fun
public inline fun
看起來陣列的map很類似,但這裡多了一個 collectionSizeOrDefault
方法,這是什麼用意呢?
很好理解:相當於可以預先設定最終容器的大小。因為有可能此 Iterable<T>
型別不是 Collection
,無法獲取 size,所以加了個判斷,如果無法獲取,則預設一個 size 為 10.
對映
Map
對映的 map
:
```kotlin
public inline fun
public inline fun
因為是 Map
,所以泛型為 Map
的 Entry
。
flatMap
同樣的,flatMap
也支援前面提到的所有型別:
kotlin
fun supportNeverEnoughForFlatMap() {
arrayOf<Int>().flatMap { 0..it }
listOf<String>().flatMap { 0..it.length }
hashMapOf<Int, String>().flatMap { 0..it.value.length }
setOf<Int>().flatMap { 0..it }
(0 until 10).flatMap { 0..it }
}
和 map
的區別在於,flatMap
的轉換函式型別是: transform: (T) -> Iterable<R>
,即輸入T型別,得到R型別的迭代集合。
所以說,map轉換是一到一,flatMap則是一到多,但是最終,flatMap
得到的還是一個R的集合
陣列型別
```kotlin
public inline fun
public inline fun
原始碼看起來和 map
是很像的,關鍵的不同處在於:
kotlin
destination.addAll(list)
因為對映結果是集合,所以這裡呼叫的是addAll
。雖然一個item得到一個集合,但最後返回值不是集合的集合,仍然是單集合 —— 很繞嗎?不,這就是flat這個字首存在的意義:扁平化。
集合型別
```kotlin
public inline fun
public inline fun
和陣列如出一轍,不用細講了。
對映
```kotlin
public inline fun
public inline fun
同樣的配方。
總有個But
但是!!!雖然不細講,細心的人還是能發現: flatMap
初始化集合全都沒指定大小 —— 因為“一到多” 的對映操作,根本無法預估最終的集合大小啊是不?
小結
看到這裡,相信 map
和 flatMap
的區別已經很清楚了吧,簡單地就如前面所說:二者區別在於轉換函式,前者“一到一”,後者“一到多”。而它們的返回型別,都是一模一樣的。
媽媽終於不用操心我的對映操作會用錯了!