Kotlin用高阶函数处理集合数据
Posted bug樱樱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin用高阶函数处理集合数据相关的知识,希望对你有一定的参考价值。
前言
一个函数f(x) 的接收参数是另一个函数(g(x))或者它的返回值是另一个函数(h(x)),在Kotlin中f(x)就是一个高阶函数。Kotlin标准库中为开发者提供了丰富的高阶函数,有sumBy, reduce, fold, map,filter,forEach等。本篇文章就来学习这些高阶函数的使用。
本文概览
过滤集合 filter
场景:输入一个账户列表List<Account>
,返回资产小于 100 的账户列表:
Java:
public List<Account> getPoorAccounts(List<Account> accounts)
List<Account> qbAccounts = new ArrayList<>(); // 声明存放目标元素的列表
for (Account a: accounts)
if (a.value < 100)
qbAccounts.add(a);
return qbAccounts;
Kotlin 可以使用filter
函数:
val qbAccounts = accounts.filter it.value < 100
filter
的逻辑是,新建一个空的 ArrayList(),然后把 lambda 返回符合条件的元素加入到列表。
遍历求和 sumBy
场景:输入一个账户列表List<Account>
,求所有账户的财产总和sum
。
Java:
public int getAccountsSum(List<Account> accounts)
int sum = 0;
for (Account a: accounts) // for循环遍历
sum += a.value;
return sum;
Kotlin 使用高阶函数 sumBy
函数:
val sum = accounts.sumBy it.value
究竟sumBy
做了什么?查看源码可知,它做的事情和上面 Java 实现的getAccountsSum
是一样的,元素值是通过传入的 lambda 来计算,而不是写死的Account.value
。
通过传入函数来完成函数功能的函数,被称为高阶函数,高阶函数也因此具有很高的通用性和复用效率。
不仅传入函数作为参数的函数被称为高阶函数,返回值为函数的函数也同样被称为高阶函数。
遍历求值 reduce
sumBy
有局限性,只能求和,且只接受Int
和Double
两种类型的值。若需要计算其他类型或者更复杂的情况呢?
场景:输入一个列表List,返回它们全部相乘的结果。
Java:
public int getResult(List<int> values)
int result = 0;
for (Account a: accounts)
result *= values;
return result;
Kotlin 可以使用reduce函数:
val result = values.reduce acc, v -> acc * v
reduce
的处理:将初始值acc设置为集合的第一个值,然后从第二个值开始,依次执行acc = lambda(acc, v)
,遍历完后返回acc。
reduce
不仅限做加法运算,比sumBy更具有通用性。
值得注意一点:如果集合为空,reduce会抛出 UnsupportedOperationException(“Empty collection can’t be reduced.”)
遍历求值 fold函数
上面sumBy
函数场景和reduce函数场景用的是不同的数据结构。acc会被初始化为集合的第一个元素,所以reduce函数的输出也被限制为集合的范型类型。也就是说,sumBy无法用reduce代替。
Kotlin 有没有能指定acc类型的高阶函数?是有的,叫fold
函数。
再回到sumBy
的场景:输入一个账户列表List<Account>
,求这些账户的财产总和sum:
val result = accounts.fold(0) acc, v -> acc + v.value
fold比reduce多了一个参数——初始值,用来赋值给acc。所以可以通过这个参数来指定acc的类型。这样一来,fold可以替代sumBy的场景。
fold还有另一点好:acc由传入参数初始化,所以没有集合不能为空的限制。大部分情况下,建议使用fold来代替reduce。
列表返回列表 map函数
业务场景:输入账户列表List,找到所有资产大于 10000 的账户,封装成 VIP 列表返回:
Java:
public List<VipAccount> getVipAccounts(List<Account> accounts)
List<VipAccount> vipAccounts = new ArrayList<>();// 声明VIP列表
for (Account a: accounts)
if (a.value >= 10000)
vipAccounts.add(new VipAccount(a));// 插入元素
return vipAccounts;
Kotlin 可以通过filter函数加map函数完成:
val vipAccounts = accounts
.filter it.value >= 10000 // 过滤条件
.map VipAccount(it)
先用filter函数筛选出符合条件的账户,然后用map函数将过滤后的每一个账户转换为VipAccount
。map的逻辑较为简单,返回一个和调用者相同大小的列表,具体元素值为 lambda 的执行结果。
用 forEach 代替 for
在部分循环遍历场景,不妨试试forEach代替传统的 for 循环,会简洁很多。
accounts.forEach
println("account: $it")
什么?还想要 index下标?
accounts.forEachIndexed index, account ->
println("index: $index")
println("account: $account")
其实不仅forEach
有下标版本forEachIndexed
,几乎所有高阶函数都有对应的 Indexed 版本。可以尝试调取一下。
Kotlin 官方提供了数十个高阶函数,但其实掌握了以上几个常用高阶函数,基本可以适用场景了。
担心性能问题?
可能会担心,如此频繁的声明 lambda
,会不会使得类的数量膨胀?官方提供的高阶函数,其实都是用inline关键字修饰的。意味着不仅高阶函数的调用会被函数的实际代码代替,而且声明的 lambda 也会被解析成具体的代码,而不是方法调用。Kotlin 高阶函数用 inline 关键字修饰,所以 lambda 不会生成新的 jvm class。在声明自己的高阶函数时,也建议用inline关键字修饰,防止类数量暴增。
还有一点,像map,filter这样返回列表的高阶函数,每一次操作都会产生一个列表,这会不会增加垃圾回收的压力?答案是会的。但如果数据量不是万级别的,操作频率不是毫秒级别的,对性能的影响其实是很小的,是在移动端的场景更是难以遇到。但了解高阶函数对性能开销是很有必要的,可以避免一些耗性能的操作。
作者:子不语Any
链接:https://juejin.cn/post/7136004981932425253
最后
Kotlin 通常被视为下一个 Java,在 StackOverflow 的 2019 年开发人员调查中,Kotlin 成为第四大“最受欢迎”和第五大“想要”的编程语言,在所有移动编程语言中排名最高。Android 开发由 Java 转 Kotlin 早已势不可挡。
在这里给大家分享由谷歌工程师整理的总结的《Kotlin从入门到精通》及《Kotlin高级强化实战》资料,帮助大家用最少的精力入门并掌握Kotlin编程语言。
一、《Kotlin从入门到精通》
目录
该内容会使用Kotlin作为主要的语言来开发一个android应用。方式是通过开发一个应用来学习这门语言,而不是根据传统的结构来学习。
现在你知道使用Kotlin实现的小例子了,我确信你会希望尽可能快地把它用在你的实践当中去。不要担心,在第一章中会帮助你去搭建你的开发环境,这样你才能立即编写代码。
从Kotlin语法概念的理解项目的创建再到整个项目的完成,每篇都是是采用“一对一”的讲解方式, 侧重总结工作上的实践经验,并和你分享一些疑难问题的解决思路,让你在以后的工作中,能够有方法论的指导。
二、《Kotlin高级强化实战》
第一章 Kotlin入门教程
- Kotlin 概述
- Kotlin 与 Java 比较
- 巧用 Android Studio
- 认识 Kotlin 基本类型
- 走进 Kotlin 的数组
- 走进 Kotlin 的集合
- 集合问题
- 完整代码
- 基础语法
第二章 Kotlin 实战避坑指南
- 方法入参是常量,不可修改
- 不要 Companion 、INSTANCE ?
- Java 重载,在 Kotlin 中怎么巧妙过渡一下?
- Kotlin 中的判空姿势
- Kotlin 复写 Java 父类中的方法
- Kotlin “狠”起来,连TODO 都不放过!
- is、as` 中的坑
- Kotlin 中的 Property 的理解
- also 关键字
- takeIf 关键字
- takeIf 关键字
- 单例模式的写法
“工欲善其事,必先利其器”,学习以上知识点,可以让你在接下来的实战学习中,事半功倍。
第三章 项目实战《Kotlin Jetpack 实战》
- 从一个膜拜大神的 Demo 开始
- Kotlin 写 Gradle 脚本是一种什么体验?
- Kotlin 编程的三重境界
- Kotlin 高阶函数
- Kotlin 泛型
- Kotlin 扩展
- Kotlin 委托
- 协程“不为人知”的调试技巧
- 图解协程:suspend
由于文章篇幅有限,资料内容过多,只展示目录和部分截图,需要该资料完整版的小伙伴可扫描下方CSDN官方认证二维码免费领取!!
希望这篇文章会对你学习和掌握 Kotlin 语言有所帮助,也希望各位读者能在Android开发的进阶之路上走得长远,共勉!!
以上是关于Kotlin用高阶函数处理集合数据的主要内容,如果未能解决你的问题,请参考以下文章