kotlin修炼指南8—聚集中的高阶函数

1个月前 (11-17 23:54)阅读3回复0
丸子
丸子
  • 管理员
  • 注册排名9
  • 经验值79040
  • 级别管理员
  • 主题15808
  • 回复0
楼主

点击上方 蓝字存眷我,常识会给你力量

Kotlin对聚集操做类新增了良多快速的高阶函数操做,各类操做符让良多开发者傻傻分不清,出格是看一些Kotlin的源码或者是协程的源码,各类目炫缭乱的操做符,让代码完全读不下去,所以,本文将对Kotlin中的聚集高阶函数,停止下讲解,降低各人阅读源码的难度,下面看几个用的比力多的高阶函数利用。

起首是sumOf,做为一个很便利的乞降函数,它能够快速对聚集内的某些参数停止sum操做,代码如下所示。

val list = mutableListOf(1, 2, 3, 4)

val sumOf = list.sumOf { it }

我们来看看它的源码。

public inline fun T IterableT.sumOf(selector: (T) - Int): Int {

var sum: Int = 0.toInt

for(element inthis) {

sum += selector(element)

returnsum

其实它内部就是对元素的累加,像如许的高阶函数,在Kotlin中有良多,那也是良多根底功用用Kotlin开发会愈加便利的原因之一。

但是sumOf有个局限,那就是只能乞降,究竟结果它设想就是用来做乞降的,所以关于愈加一般的场景,我们能够将那个操做再进一步笼统出来,那就是reduce。

好比我们如今要实现一个乘法功用,代码如下所示。

val list = mutableListOf(1, 2, 3, 4)

val result = list.reduce { acc, i -

acc * i

reduce的操做参数有两个,当前的累积值和聚集中的下一个元素。reduce的施行逻辑是,先取出聚集的第一个元素,做为acc,并和第二个元素——i,施行block中的逻辑,返回值做为acc,继续上面的步调。

展开全文

若是聚集为空,那么会招致异常。

若是聚集为空,那么会招致异常。

但是reduce也有个局限问题,那就是它默认利用聚集的第一个元素做为起始的acc,所以它就只能返回前面聚集的泛型类型,假设是下面如许的构造,就无法利用了。

data class Test(val num: Int, val name: String)

val list = mutableListOf(

Test(1, "x"),

Test(2, "y"),

Test(3, "z"),

Test(4, "j")

val result = list.reduce { acc, i -

acc.num * i.num // Error

其问题,就是在于acc的类型不克不及指定,只能从聚集中获取,所以,Kotlin还供给了愈加通用的高阶函数——fold,代码如下所示。

data class Test(val num: Int, val name: String)

val list = mutableListOf(

Test(1, "x"),

Test(2, "y"),

Test(3, "z"),

Test(4, "j")

val result = list.fold(1) { acc, test-

acc * test.num

fold和reduce十分像,只不外fold增加了一个initial的参数,通过那个参数,能够设置acc的初始值,同时也指定了返回的类型,如许一来,就不像reduce一样需要和聚集类型连结一致了。

因为初始值是initial参数指定的,所以即便聚集为空也不会招致异常。

因为初始值是initial参数指定的,所以即便聚集为空也不会招致异常。

由此可见,在Kotlin中,reduce现实上是一个不完美的高阶函数,大部门时候,都应该制止利用它,而应该利用flod来取代,并且,要留意的是,在其它语言中,例如Java中,它的reduce函数,现实上和Kotlin的fold函数的逻辑是一样的,而不是Kotlin中reduce的实现。

那么fold有什么利用场景呢?前面说的对聚集停止遍历,然后对某些项目停止乞降、求积、拼接字符串那些操做,就是一个十分常用的例子。

和大部门的聚集高阶函数一样,fold也供给了foldRight、foldIndexed、foldRightIndexed如许的拓展,能够通过获取索引,或者是改动遍历的标的目的。

和大部门的聚集高阶函数一样,fold也供给了foldRight、foldIndexed、foldRightIndexed如许的拓展,能够通过获取索引,或者是改动遍历的标的目的。

fold和reduce,现实上是一种对聚集的规约操做,最初会返回一个「规约」之后的值,相当于对聚集做提取并规约的操做。

除了对聚集的规约,对聚集的遍历,Kotlin也做了良多改进。

例如我们能够通过filter来过滤聚集中称心某些规则的元素,代码如下所示。

val result = list.filter { it.num 2 }

再例如对聚集做排序,固然之前也能做,但是绝对不像高阶函数如许一目了然,让我们看下下面的代码。

val sorted = lists.sorted

val sortedDescending = lists.sortedDescending

val sortedBy = lists.sortedBy { it.length }

val sortedByDescending = lists.sortedByDescending { it.length }

val sortedWith = lists.sortedWith(

compareByString { it.length }.thenBy { it }

那些都是常见的排序办法,根本能够涵盖我们大部门的利用场景。

除了排序,我们还能够对聚集做Check,判断聚集中能否有称心前提的元素,例如下面的代码。

val any = lists.any { it.length == 6 }

val all = lists.all { it.length == 6 }

val none = lists.none { it.length == 6 }

val count = lists.count { it.length == 6 }

类似的例如Search和take如许的高阶函数我们就不讲了,根本都能够望文生义。

最初我们来看下聚集中的Transform。

最简单的,我们能够借助map函数来对一个聚集做转换,例如下面的代码。

val result = list.map { it.num }

如许就构成了一个num构成的新聚集。

map相对来说比力好理解,它实现的是一对一的转换,但是另一个——flatMap就不是那么好理解了。

所以我们先来领会另一个操做符——flatten。

假设我们有如许一个嵌套的List,如下所示。

val list = listOf(listOf( "abc", "xyz", "hjk"), listOf( "123", "789"), listOf( "+-"))

我需要将那个二维List打平为一个一维List,那么就能够通过flatten来实现,代码如下所示。

val result = list.flatten

// out

[abc, xyz, hjk, 123, 789, +-]

那么若是我在打平List之后,还要对数据做一些处置呢?很便利,我们能够链式挪用其它的高阶函数,例如map,代码如下所示。

val result = list.flatten.map { it.first }

// out

[a, x, h, 1, 7, +]

如许的操做其实很常见,所以Kotlin供给了一个复合的高阶函数——flatMap,我们利用flatMap来实现同样的功用。

val result = list.flatMap {

it.map { item -

item.first

它现实上就是先利用flatten将数据打平,再对每个item停止map操做。所以,若是你只是需要打平数据,那么间接flatten就够了,若是需要再对数据做一些处置,那么就需要利用flatMap了。

flatMap的一个十分常用的场景,就是生成两个List的叉乘数据,我们来看下面那个例子。

val SUITS = setOf( "♣"/* clubs*/, "♦"/* diamonds*/, "♥"/* hearts*/, "♠"/*spades*/)

val VALUES = setOf( "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A")

上面有两个list,别离是扑克牌中的花色和数字,那么我们若何通过那两个list来生成整副扑克牌呢?借助flatMap就能够很便利的实现,代码如下所示。

val DECK = SUITS.flatMap { suit -

VALUES.map { value - Card(suit, value) }

那个例子和上面的例子还不太一样,能够说是一个互逆的过程,前面我们是通过一个嵌套List,然后打平处置数据,而那个例子,则是两个list停止叉乘,生成一个新的List。

综上,我们总结下flatMap的工做流程,起首,flatMap会遍历聚集中的元素,然后将每个元素传入block中,颠末block处置后返回一个list,最初将每个元素处置完后生成的list停止平铺,生成一个打平的list,那就是flatMap的完好施行流程。

由此可见,大部门场景下,我们以至都不消再利用聚集的遍历功用,通过那些辅助的高阶函数,就能够很便利的对聚集停止操做,那也是Kotlin代码会比Java愈加容易开发的原因,当然,Kotlin的函数式编程体例,会比Java的上手难度更高。

那么我们在利用Kotlin的高阶函数来对聚集停止处置时,能否需要担忧一些隐藏的性能开销呢?

起首,Kotlin默认的聚集类高阶函数,都是inline函数,所以在编译时会停止替代,从而高阶函数的block不会生成新的内部类,形成代码膨胀,但是,因为高阶函数每次处置聚集时,城市产生一个新的聚集,所以确实会形成内存的增长,但是关于挪动端来说,在数据量不大的场景下,那个影响是微乎其微的,所以,完全不消担忧性能的开销,安心斗胆的利用吧。

向各人保举下我的网站 /点击原文一键中转

专注 Android-Kotlin-Flutter 欢送各人拜候

往期保举

忙里偷闲IdleHandler

Android-Widget重拆上阵

Android壁纸仍是B站玩得花

Flutter混编工程之打通纹理之路

本文原创公家号:群英传,受权转载请联络微信(Tomcat_xu),受权后,请在原创颁发24小时后转载。

END

做者:徐宜生

更文不容易,点个“三连”撑持一下👇

0
回帖

kotlin修炼指南8—聚集中的高阶函数 期待您的回复!

取消
载入表情清单……
载入颜色清单……
插入网络图片

取消确定

图片上传中
编辑器信息
提示信息