持续创作,加速成长!这是我参与「日新计划 6 月更文挑战」的第9天,点击查看活动详情。
Kotlin
的官方扩展函数一直给我的感觉就是:简单、好用还呈现一种“只有你想不到没有你找不到”的态势。
今天咱不聊多么复杂的,就来谈谈 m数组初始化ap
和 flatMap
,名字很像,到底这两个货有什么区别呢?分别面对的又是什么使用场景?
概述
首先,名字即功能,map
和 flatMap
是为实现“映射”而存在的,官网是这么描述映射的:
The mapping transformation creates a collection from the resuelementanimationlts of a function on the elements of another collection.
意思就是:提供一个转换函数,用以把一个集合的元素转化生成另一个集合,而其中最为基础的就是 map
。
其次源码交易平台,这两个不是单属于扁平化组织结构某个特定类型的扩展,它们写出来就是面向所有可源码编辑器下载能需要有“映射”需求的地方的。不信来看看:
// 数组扩展
public inline fun <T, R> Array<out T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(size), transform)
}
// 迭代类扩展
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}
// 映射扩展
public inline fun <K, V, R> Map<out K, V>.map(transform: (Map.Entry<K, V>) -> R): List<R> {
return mapTo(ArrayList<R>(size), transform)
}
都是泛型实现,涵盖的类型可真不少,甚至还有“映射的映射”这样的。像Itera初始化电脑的后果ble<T>
这样的,一扩展,支持的类型那可多了,所有的Collections<T>
类型都源码编程器是,比如List
、Se源码1688t
;另外,Kotlin
的 Range 类型也没落下。
fun supportNeverEnough() {
arrayOf<Int>().map { }
listOf<String>().map { }
hashMapOf<Int, String>().map { }
setOf<Int>().map { }
(0 until 10).map { }
// 好了,不继续写了……
}
当然,flatMap
也是一样的。
而且值数组c语言得注意的是,不管是哪个map,其结果都是List
类型。
map
map
接受传入的转换函数,处理后,即将源elementui转化数组词成一个新的List,且这个新的List的元素顺序和其源是一致的。
对于“数组”类的源(比如list, set, array等),map
为:
fun <T, R> XXX<T>.map(transform: (T) -> R): List<R>
可以看到,map就是将T类型的集合转化成了R类型的List。
数组类型
public inline fun <T, R> Array<out T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(size), transform)
}
public inline fun <T, R, C : MutableCollection<in R>> Array<out T>.mapTo(destination: C, transform: (T) -> R): C {
for (item in this)
destination.add(transform(item))
return destination
}
map
内部会调用 Array<ouelement翻译t T>.mapTo
方法,该方法第一个参数是 MutableCollectionelementanimation<in R>
的子类型,即可变集合,用来迭代保存结果元素。
第二个参数就是转换函数,将T类型转为R类型。
内部 foelementary翻译r 循环迭代所有元素,每个元素调用转换函数,生成结果并添加至集合,最后返回。整个过程算是十分简单了。
迭代集合类型
这是针对迭代集合 Iterable
的:
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}
internal fun <T> Iterable<T>.collectionSizeOrDefault(default: Int): Int = if (this is Collection<*>) this.size else default
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很类似,但这里多了一个 collect源码交易平台ionSizeOrDefault
方法,这是什么用意呢?
很好理解:相当于可以预先设定最终容器的大小。因为有可能数组词此 Iterable<T>
类型不是 Collection
,无法获取 size,所以加了个判断,如果无扁平化管理模式法获取,则默认一个 size 为 10.
映射
Map
映射的 map
:
public inline fun <K, V, R> Map<out K, V>.map(transform: (Map.Entry<K, V>) -> R): List<R> {
return mapTo(ArrayList<R>(size), transform)
}
public inline fun <K, V, R, C : MutableCollection<in R>> Map<out K, V>.mapTo(destination: C, transform: (Map.Entry<K, V>) -> R): C {
for (item in this)
destination.add(transform(item))
return destination
}
因为是 Map
,所以泛型为 <K, V, R> , 类似数组和迭代集合,这里只是把迭代转换的参数换成了 Map
的 Entry
。
flatMap
同样的,flatMap
也支持前面提到的所有类型:
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
的区别在于,flat扁平化Map
的转换函数类型是: transform: (T) -> Iterable<R>
,即输入T类型,得到R类型的迭代集合。
所以说,map转换是一到一,flatMap则扁平化是一到多,但是最终,flatMap
得到的还是一个R的集合
数组类型
public inline fun <T, R> Array<out T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
return flatMapTo(ArrayList<R>(), transform)
}
public inline fun <T, R, C : MutableCollection<in R>> Array<out T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {
for (element in this) {
val list = transform(element)
destination.addAll(list)
}
return destination
}
源码看起来和 map
是很像的,关键的不同处在扁平化插画于:
destination.addAll(list)
因为映射结果是集合源码中的图片,所以elementary翻译这里调用的是addAll
。虽然一个item得到一个集合,但最后返回值不是集合的集合,仍然是单集合 —— 很绕吗?不,这就是flat这个前缀存在的意义:扁平化。
集合类型
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
}
和数组如出一辙,不用细讲了。源码
映射
public inline fun <K, V, R> Map<out K, V>.flatMap(transform: (Map.Entry<K, V>) -> Iterable<R>): List<R> {
return flatMapTo(ArrayList<R>(), transform)
}
public inline fun <K, V, R, C : MutableCollection<in R>> Map<out K, V>.flatMapTo(destination: C, transform: (Map.Entry<K, V>) -> Iterable<R>): C {
for (element in this) {
val list = transform(element)
destination.addAll(list)
}
return destination
}
同样源码之家的配方。
总有个But
但是!!!虽然不细讲,细心的人还是能发数组去重现: flatMap
初始化集合全都没指定大小 —— 因为“一到多” 的映射操作,根本无法预估最终的集合大小啊是不?
小结
看到这里,相信 map
和 flatMap
的区别已经很清楚了吧,简单地就如前面所说:二者区别在于转换函数,前者“一到一”,后者“一到多”。而它们的返回类型,都是一模一样的。
妈妈终于不用操心我的映射操作会用错了!