Kotlin基础——Lambda和函数式编程

Lambda

使用 { } 定义Lamba,调用run运行

run { println(1) }

更常用的为 { 参数 -> 操作 },还可以存储到变量中,把变量作为普通函数对待

val sum = { x: Int, y: Int -> x + y }
println(sum(1, 2))

maxBy()接收一个Lambda,传递如下

class Person(val name: String, val age: Int)
val people = listOf(Person("A", 18), Person("B", 19))println(people.maxBy({ p: Person -> p.age }))
println(people.maxBy() { p: Person -> p.age })println(people.maxBy { p: Person -> p.age })	//只有一个参数,可省略()
println(people.maxBy() { p -> p.age })		//可推导出类型
println(people.maxBy() { it.age })		//只有一个参数且可推导出类型,会自动生成it

Lambda可使用函数参数和局部变量

 fun printProblemCounts(response: Collection<String>) {var clientErrors = 0var serverErrors = 0response.forEach {if (it.startsWith("4")) {clientErrors++} else if (it.startsWith("5")) {serverErrors++}}}

成员引用

上面通过Lambda将代码块作为参数传递给函数,若要传递代码块已被封装成函数,则需要传递一个调用该函数的Lambda,如下计算虚岁

class Person(val name: String, val age: Int) {fun getNominalAge(): Int {return age + 1}
}val people = listOf(Person("A", 18), Person("B", 19))
println(people.maxBy { p: Person -> p.getNominalAge() })println(people.maxBy(Person::getNominalAge))		//成员引用,可以省略多余的函数调用

上面是系统为fun getNominalAge()自动生成的成员引用,实际定义应该如下,把函数转换为一个值,从而可以传递它

  • 把函数age + 1传递给getNominalAge,通过Person::getNominalAge引用函数
  • 直接通过Person::age引用成员
 class Person(val name: String, val age: Int) {val getNominalAge = age + 1}val people = listOf(Person("A", 18), Person("B", 19))println(people.maxBy(Person::getNominalAge))println(people.maxBy(Person::age))

若引用顶层函数,则可以省略类名称,以::开头

fun salute() = println("Salute")run(::salute)

集合的函数式API

filter和map

filter遍历集合并筛选指定Lambda返回true的元素,如下遍历偶数

val list = listOf(1, 2, 3, 4)
println(list.filter { it % 2 == 0 })data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 29), Person("B", 31))
println(people.filter { it.age > 30 })

map对集合中的每一个元素应用给定的函数并把结果收集到一个新的集合

val list = listOf(1, 2, 3, 4)
println(list.map { it * it })data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 29), Person("B", 31))
println(people.map(Person::name))

Lambda会隐藏底层操作,如寻找最大年龄,第一种方式会执行100遍,应该避免

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 29), Person("B", 31))
people.filter { it.age == people.maxBy(Person::age)!!.age }val maxAge = people.maxBy(Person::age)!!.age
people.filter { it.age == maxAge }

对于Map,可调用filterKeys/mapKeys、filterValues/mapValues

val numbers = mapOf(0 to "zero", 1 to "one")
println(numbers.mapValues { it.value.toUpperCase() })

all、any、count、find

all判断集合所有元素是否都满足条件,any判断至少存在一个满足条件的元素

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 26), Person("B", 27))val max27 = { p: Person -> p.age >= 27 }
println(people.all(max27))
println(people.any(max27))

!all()表示不是所有符合条件,可用any对条件取反来代替,后者更容易理解

val list = listOf(1, 2, 3)
println(!list.all { it == 3 })
println(list.any { it != 3 })

count用于获取满足条件元素的个数,其通过跟踪匹配元素的数量,不关心元素本身,更加高效,若使用size则会创建临时集合存储所有满足条件的元素

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 26), Person("B", 27))
val max27 = { p: Person -> p.age >= 27 }println(people.count(max27))
println(people.filter(max27).size)

find找到一个满足条件的元素,若有多个则返回第一个,否则返回null,同义函数firstOrNull

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 26), Person("B", 27))
val max27 = { p: Person -> p.age >= 27 }println(people.find(max27))
println(people.firstOrNull(max27))

groupby

groupby把列表转成分组的map

data class Person(val name: String, val age: Int)val people = listOf(Person("A", 26), Person("B", 27), Person("C", 27))
println(people.groupBy { it.age })

如上打印

{
26=[Person(name=A, age=26)], 
27=[Person(name=B, age=27), Person(name=C, age=27)]
}

flatMap、flatten

flatMap根据给定的函数对集合中的每个元素做映射,然后将集合合并,如下打印 [A, 1, B, 2, C, 3]

val strings = listOf("A1", "B2", "C3")
println(strings.flatMap { it.toList() })

如统计图书馆书籍的所有作者,使用Set去除重复元素

data class Book(val title: String, val authors: List<String>)
val books = listOf(Book("A", listOf("Tom")),Book("B", listOf("john")),Book("C", listOf("Tom", "john"))
)
println(books.flatMap { it.authors }.toSet())

flatten用于合并集合,如下打印 [A1, B2, C3]

val strings = listOf("A1", "B2", "C3")
println(listOf(strings).flatten())

序列

序列的好处

map / filter 会创建临时的中间集合,如下就创建了2个

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 26), Person("B", 27))
println(people.map(Person::age).filter { it > 26 })

而使用序列可以避免创建

println(people.asSequence().map(Person::age).filter { it > 26 }.toList())

惰性操作及性能

序列的中间操作都是惰性的,如下不会有输出

listOf(1, 2, 3, 4).asSequence().map { println("map($it)"); it * it }.filter { println("filter($it)");it % 2 == 0 }

只有当末端操作时才会被调用,如toList()

listOf(1, 2, 3, 4).asSequence().map { println("map($it)"); it * it }.filter { println("filter($it)");it % 2 == 0 }.toList()
  • 序列先处理第一个元素,然后再处理第二个元素,故可能有些元素不会被处理,或轮到它们之前就已经返回
  • 若不使用序列,则会先求出map的中间集合,对其调用find
println(listOf(1, 2, 3, 4).asSequence().map { print(" map($it)");it * it }.find { it > 3 })
println(listOf(1, 2, 3, 4).map { print(" map($it)");it * it }.find { it > 3 })

如上都打印4,但序列运行到第二个时已找到满足条件,后面不会再执行

map(1) map(2)4
map(1) map(2) map(3) map(4)4

序列的顺序也会影响性能,第二种方式先filter再map,所执行的变换次数更少

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 26), Person("AB", 27), Person("ABC", 26), Person("AB", 27))println(people.asSequence().map(Person::name).filter { it.length < 2 }.toList())println(people.asSequence().filter { it.name.length < 2 }.map(Person::name).toList())

创建序列

generateSequence根据前一个元素计算下一个元素

val naturalNumbers = generateSequence(0) { it + 1 }
val numbersTo100 = naturalNumbers.takeWhile { it <= 100 }
println(numbersTo100.sum())

和Java一起使用

函数式接口(SAM接口)

若存在如下Java函数

public class Test {public static void run(int delay, Runnable runnable) {try {Thread.sleep(delay);} catch (InterruptedException e) {e.printStackTrace();}runnable.run();}
}

对于上面接受Runnable的接口,可以传递Lambda或创建实例,前者不会创建新的实例,后者每次都会创建

Test.run(1000, Runnable { println("Kotlin") })
Test.run(1000, { println("Kotlin") })
Test.run(1000) { println("Kotlin") }    //最优Test.run(1000, object : Runnable {override fun run() {println("Kotlin")}
})

若Lambda捕捉到了变量,每次调用会创建一个新对象,存储被捕捉变量的值

  • 若捕捉了变量,则Lambda会被编译成一个匿名类,否则编译成单例
  • 若将Lambda传递给inline函数,则不会创建任何匿名类
fun handleRun(msg: String) {Test.run(1000) { println(msg) }
}

SAM构造方法

大多数情况下,Lambda到函数式接口实例的转换都是自动的,但有时候也需要显示转换,即使用SAM构造方法,其名称和函数式接口一样,接收一个用于函数式接口的Lambda,并返回这个函数式接口的实例

val runnable = Runnable { println("Kotlin") }   //SAMrunnable.run()

如下使用SAM构造方法简化监听事件

val listener = View.OnClickListener { view ->val text = when (view.id) {1 -> "1"else -> "-1"}println(text)
}

带接收者的Lambda

with

fun alphabet(): String {val result = StringBuilder()for (letter in 'A'..'Z') {result.append(letter)}result.append("\nover")return result.toString()
}

上面代码多次重复result这个名称,使用with可以简化,内部可用this调用方法或省略

  • with接收两个参数,下面例子参数为StringBuilder和Lambda,但把Lambda放在外面
  • with把第一个参数转换成第二个参数Lambda的接收者
fun alphabet(): String {val result = StringBuilder()return with(result) {for (letter in 'A'..'Z') {this.append(letter)}append("\nover")toString()}
}

还可以进一步优化

fun alphabet() = with(StringBuilder()) {for (letter in 'A'..'Z') {this.append(letter)}append("\nover")toString()
}

apply

with返回的是接收者对象,而不是执行Lambda的结果,而使用apply()会返回接收者对象,可以对任意对象上使用创建对象实例,还能代替Java的Builder

fun alphabet() = StringBuilder().apply {for (letter in 'A'..'Z') {this.append(letter)}append("\nover")
}.toString()

使用库函数buildString还可以简化上述操作

fun alphabet() = buildString {for (letter in 'A'..'Z') {this.append(letter)}append("\nover")
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/166238.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EXIT外部中断 HAL库+cubeMX

一.cubeMX外部中断配置 1.系统内核 2.中断管理 3.选择抢占优先级和响应优先级&#xff0c;共有5个等级&#xff0c;在这里就使用库函数编写代码时最常用的2位抢占优先级2位响应优先级。 4.勾选使能选项&#xff0c;后面的两个零&#xff0c;第一个代表抢占优先级的等级&#xf…

用于计算机屏幕安全摄像头系统:Screen Anytime Crack

Screen Anytime 是一款软件&#xff0c;旨在自动将整个用户会话或 PC/服务器/VM/Kiosk 的 /RDP/Citrix/RemoteApp 会话的屏幕活动记录到视频日志文件中&#xff0c;以用于记录、审核和监控目的。通过重播其高度压缩的视频&#xff0c;您可以轻松回顾单台计算机或一组服务器/PC …

基于C#实现并查集

一、场景 有时候我们会遇到这样的场景&#xff0c;比如:M{1,4,6,8},N{2,4,5,7}&#xff0c;我的需求就是判断{1,2}是否属于同一个集合&#xff0c;当然实现方法有很多&#xff0c;一般情况下&#xff0c;普通青年会做出 O(MN)的复杂度&#xff0c;那么有没有更轻量级的复杂度呢…

Vatee万腾科技的独特力量:Vatee数字时代创新的新视野

在数字化时代的浪潮中&#xff0c;Vatee万腾科技以其独特而强大的创新力量&#xff0c;为整个行业描绘了一幅崭新的视野。这不仅是一场科技创新的冒险&#xff0c;更是对未来数字时代发展方向的领先探索。 Vatee万腾将创新视为数字时代发展的引擎&#xff0c;成为推动行业向前的…

ubuntu 安装python3.13

列出 /usr/bin/ 目录下所有以 python 开头的文件和目录 ls /usr/bin/python* 添加Python软件源。您可以通过以下命令将Python的软件源添加到您的系统中 sudo add-apt-repository ppa:deadsnakes/ppa 然后运行以下命令以更新软件包列表&#xff1a; sudo apt-get update 安…

vue每个阶段的生命周期做了什么

Vue 实例的生命周期可以分为创建阶段、挂载阶段、更新阶段和销毁阶段。下面是每个阶段具体干了什么的说明和对应的代码示例&#xff1a; 创建阶段 beforeCreate&#xff1a; 此阶段在实例初始化之后&#xff0c;数据观测 (data observer) 和 event/watcher 事件配置之前被调用…

Spring AOP 底层原理

Spring AOP 底层原理 aop 底层是采用动态代理机制实现的&#xff1a;接口实现类 &#xff08;1&#xff09;如果要代理的对象&#xff0c;实现了某个接口&#xff0c;那么 Spring AOP 会使用 JDK Proxy&#xff0c;去创建代理对象。 &#xff08;2&#xff09;没有实现接口的对…

下一代ETL工具:微服务架构的全新数据集成平台

当前对于大型企业来说数据的整合和加工变得越来越重要。随着业务需求的不断增长&#xff0c;企业数据量越来越大&#xff0c;数据管道越来越多&#xff0c;现有的ETL&#xff08;抽取、转换、加载&#xff09;工具已不再满足实时、高性能和微服务架构等现代化需求。因此&#x…

基于C#实现Prim算法

图论在数据结构中是非常有趣而复杂的&#xff0c;作为 Web 码农的我&#xff0c;在实际开发中一直没有找到它的使用场景&#xff0c;不像树那样的频繁使用&#xff0c;不过还是准备仔细的把图论全部过一遍。 一、最小生成树 图中有一个好玩的东西叫做生成树&#xff0c;就是用…

前端项目搭建Webpack的配置

本人这次是在搭建一个Typescript项目时候配置的Webpack。但是Typescript的项目本人看来和往常的Web(Vue)项目类似点很多的。那么我们就可以通过对该Typescript项目的略微调整即可挪到Web项目中....... 首先说明一下为什么要依赖WebPack来搭建项目&#xff1f;&#xff1f;&…

ES 万条以外分页检索功能实现及注意事项

背景 以 ES 存储日志&#xff0c;且需要对日志进行分页检索&#xff0c;当数据量过大时&#xff0c;就面临 ES 万条以外的数据检索问题&#xff0c;如何利用滚动检索实现这个需求呢&#xff1f;本文介绍 ES 分页检索万条以外的数据实现方法及注意事项。 需求分析 用 ES 存储数…

Redis 反序列化失败

文章目录 问题原序列化配置修改配置解决方法 问题 com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of org.springframework.security.core.authority.SimpleGrantedAuthority (although at least one Creator exists): cannot deser…

css图片缩放属性object-fit说明

object-fit 属性可以设置以下值&#xff1a; 属性值说明例子fill填充容器&#xff0c;可能会改变图片的比例。object-fit: fill;contain保持图片的原始比例&#xff0c;确保图片完全包含在容器内。object-fit: contain;cover保持图片的原始比例&#xff0c;确保图片覆盖整个容…

性能优化中使用Profiler进行页面卡顿的排查及解决方式

文章目录 一、前言二、页面卡顿的排查方式1、耗时操作的监控2、页面卡顿的监控 三、参考链接 一、前言 程序的优化在做过线上bug处理&#xff0c;布局层级优化&#xff0c;项目依赖库版本更新&#xff0c;重复库合并&#xff0c;删除未使用的资源&#xff0c;删除冗余的库&…

机器学习【01】相关环境的安装

学习实例 参考资料&#xff1a;联邦学习实战{杨强}https://book.douban.com/subject/35436587/ 项目地址&#xff1a;https://github.com/FederatedAI/Practicing-Federated-Learning/tree/main/chapter03_Python_image_classification 一、环境准备 GPU安装CUDA、cuDNN pytho…

ComboGrid中快捷键Enter使用

为了实现当前元素&#xff0c;回车时有值跳转到下一个元素&#xff0c;无值则查询。 定义元素时使用快捷键 $.fn.combogrid.defaults.keyHandler.up.call(this);调用combogrid默认的快捷键 $(#cs).combogrid({width: 360,placeholder: 测试...,panelWidth: 1000,qParams: {pJ…

letcode::数组中的第k个最大元素

数组中的第k个最大元素 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1: 输入: …

PHP 语法||PHP 变量

PHP 脚本在服务器上执行&#xff0c;然后将纯 HTML 结果发送回浏览器。 基本的 PHP 语法 PHP 脚本可以放在文档中的任何位置。 PHP 脚本以 <?php 开始&#xff0c;以 ?> 结束&#xff1a; <?php // PHP 代码 ?> 值得一提的是&#xff0c;通过设定php.ini的相…

nvm-切换node版本工具安装-方便好用

去官网下载&#xff1a; https://github.com/coreybutler/nvm-windows#installation--upgrades 网站进去后点击下载&#xff0c;点击那个exe文件就下载本地&#xff0c;然后双击安装 安装nvm 就直接按照窗口提示的下一步就行&#xff0c;如果改了某些地方会不成功&#xf…