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

文章详细解析了Kotlin中的map和flatMap两个扩展函数,它们都是用于集合的映射操作,但map是一对一转换,结果仍为列表,而flatMap则支持一对多转换,会将结果扁平化成单个列表。这两个函数在处理数组、迭代器和Map时有不同的实现,但都提供了泛型支持,适用于多种数据类型。
摘要由CSDN通过智能技术生成

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

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

概述

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

The mapping transformation creates a collection from the results 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)
}
复制代码

都是泛型实现,涵盖的类型可真不少,甚至还有“映射的映射”这样的。像Iterable<T>这样的,一扩展,支持的类型那可多了,所有的Collections<T>类型都是,比如ListSet;另外,Kotlin 的 Range 类型也没落下。

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 为:

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<out T>.mapTo 方法,该方法第一个参数是 MutableCollection<in R> 的子类型,即可变集合,用来迭代保存结果元素。

第二个参数就是转换函数,将T类型转为R类型。

内部 for 循环迭代所有元素,每个元素调用转换函数,生成结果并添加至集合,最后返回。整个过程算是十分简单了。

迭代集合类型

这是针对迭代集合 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很类似,但这里多了一个 collectionSizeOrDefault 方法,这是什么用意呢?

很好理解:相当于可以预先设定最终容器的大小。因为有可能此 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 的区别在于,flatMap 的转换函数类型是: 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)
复制代码

因为映射结果是集合,所以这里调用的是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 的区别已经很清楚了吧,简单地就如前面所说:二者区别在于转换函数,前者“一到一”,后者“一到多”。而它们的返回类型,都是一模一样的。

作者:王可大虾
链接:https://juejin.cn/post/7112716975230418980
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

通过使用flatMap函数,可以将一个列表中的每个元素进行变换,并将结果合并为一个新的列表。flatMap函数将每个元素映射为一个列表,并将这些列表合并为一个新的列表。在引用和中提供了使用flatMap函数的示例代码。在这些示例中,我们有一个整数列表list,通过使用flatMap函数,将每个元素映射为一个新的列表,并将这些列表合并为一个新的列表。在这个新的列表中,我们可以对每个元素进行操作或者打印。flatMap函数可以用于处理复杂的数据操作,例如列表中的嵌套列表。在引用中提供了更多关于Kotlin List的使用示例。123 #### 引用[.reference_title] - *1* *2* [kotlin有关mapflatMap](https://blog.csdn.net/new_abc/article/details/82983844)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] - *3* [ Kotlin List 的使用 2:associateBy,groupBy,partition,flatMap,minOrNull,sorted,zip](https://blog.csdn.net/zyctimes/article/details/127814401)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值