一次性讨论清楚Kotlin的map和flatMap

持续创作,加速成长!这是我参与「日新计划 6 月更文挑战」的第9天,点击查看活动详情。

Kotlin 的官方扩展函数一直给我的感觉就是:简单、好用还呈现一种“只有你想不到没有你找不到”的态势。

今天咱不聊多么复杂的,就来谈谈 m数组初始化apflatMap,名字很像,到底这两个货有什么区别呢?分别面对的又是什么使用场景?

概述

首先,名字即功能,mapflatMap是为实现“映射”而存在的,官网是这么描述映射的:

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>类型都源码编程器是,比如ListSe源码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> , 类似数组和迭代集合,这里只是把迭代转换的参数换成了 MapEntry

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 初始化集合全都没指定大小 —— 因为“一到多” 的映射操作,根本无法预估最终的集合大小啊是不?

小结

看到这里,相信 mapflatMap 的区别已经很清楚了吧,简单地就如前面所说:二者区别在于转换函数,前者“一到一”,后者“一到多”。而它们的返回类型,都是一模一样的。

妈妈终于不用操心我的映射操作会用错了!

发表评论

提供最优质的资源集合

立即查看 了解详情