當Flow遇上廚房:解密Kotlin的"料理魔法"—map與flatMap
序幕:披薩店的訂單危機
想象你經營著一家網紅披薩店:
- ? ???? 后廚流水線:每分鐘處理10個訂單(map大廚)
- ? ?? 外賣騎手團:每分鐘只能配送2個訂單(flatMap騎手)
- ? ?? 災難現場:訂單堆積成山,顧客投訴電話被打爆...
這就是編程世界中的數據流轉危機!今天我們用廚房故事,揭秘map
和flatMap
這對"料理兄弟"的絕活。
料理兄弟的絕技
map大廚——精準的"食材翻譯官"
// 把普通披薩升級為豪華版
fun upgradePizza() = flowOf("培根披薩", "水果披薩", "海鮮披薩")
.map { pizza ->
"超級至尊$pizza" // 給每個披薩加前綴
}
.collect { println("?? 出品:$it") }
料理過程解析:
- ? 輸入:["培根", "水果", "海鮮"]
- ? 加工:給每個披薩加上"超級至尊"頭銜
- ? 輸出:["超級至尊培根披薩", ...](數量不變,1:1轉換)
適用場景:菜單翻譯、價格換算、單位轉換—所有需要等量變形的操作
flatMap騎手——神奇的"訂單分身術"
// 把組合套餐拆分成單品
fun unpackCombo() = flowOf("全家福套餐", "情侶套餐")
.flatMapConcat { combo ->
when(combo) {
"全家福套餐" -> flowOf("大披薩", "薯條", "可樂×4")
"情侶套餐" -> flowOf("雙拼披薩", "紅酒")
else -> emptyFlow()
}
}
.collect { println("?? 分裝:$it") }
魔法效果:
? 輸入:2個套餐訂單
? 加工:拆解成7個單品
? 輸出:["大披薩", "薯條", ...](數量變化,1:N轉換)
適用場景:套餐拆解、批量下載、嵌套數據處理—需要一對多轉換的操作
騎手團隊的三大流派
順序騎手(flatMapConcat)
// 嚴格按照訂單順序配送
flowOf("訂單A", "訂單B")
.flatMapConcat { order ->
flow {
emit("$order-包裹1")
delay(1000) // 模擬裝車耗時
emit("$order-包裹2")
}
}
.collect { println("?? 順序送達:$it") }
配送日志:
?? 順序送達:訂單A-包裹1
(等待1秒)
?? 順序送達:訂單A-包裹2
?? 順序送達:訂單B-包裹1
(等待1秒)
?? 順序送達:訂單B-包裹2
閃電騎手(flatMapMerge)
// 多訂單并行配送
flowOf("訂單A", "訂單B")
.flatMapMerge { order ->
flow {
emit("$order-包裹1")
delay(Random.nextLong(500,1500))
emit("$order-包裹2")
}
}
.collect { println("? 并行送達:$it") }
可能的結果:
? 并行送達:訂單A-包裹1
? 并行送達:訂單B-包裹1
? 并行送達:訂單B-包裹2
? 并行送達:訂單A-包裹2
霸道騎手(flatMapLatest)
// 新訂單優先原則
flow {
emit("舊訂單")
delay(500)
emit("新訂單")
}
.flatMapLatest { order ->
flow {
repeat(3) {
delay(1000)
emit("處理中:$order-階段${it+1}")
}
}
}
.collect { println("?? 優先處理:$it") }
殘酷現實:
?? 優先處理:舊訂單-階段1
(新訂單到達,舊訂單被取消)
?? 優先處理:新訂單-階段1
?? 優先處理:新訂單-階段2
?? 優先處理:新訂單-階段3
終極選擇題:你該召喚誰?
當遇到以下場景時,請選擇你的"料理助手":
? 需要把用戶ID列表轉換為用戶詳情列表 → map
? 需要根據每個用戶ID獲取其歷史訂單 → flatMap
? 實時股票報價,只需顯示最新價格 → flatMapLatest
? 批量下載用戶相冊中的所有圖片 → flatMapMerge
(答案:全選正確!?)
新手: "昨天用map處理API響應,結果內存炸了..."
老手: "你肯定把整個JSON數組都map了吧?試試flatMap拆分成數據流!"
架構師: "記住:map是1:1轉換,flatMap是1:N的魔法通道!"
產品經理: "我只要結果能跑,管你們用map還是按摩(massage)!"