热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Kotlin中的Lambda编程

文章目录1.集合的创建与遍历2.集合的函数式API3.Java函数式API的使用1.集合的创建与遍历传统意义上的集合主要是List和Set,再广泛一点的话ÿ


文章目录


      • 1.集合的创建与遍历
      • 2.集合的函数式API
      • 3.Java函数式API的使用





1.集合的创建与遍历

传统意义上的集合主要是List和Set,再广泛一点的话,像Map这样的键值对数据结构也可以包含进来。List,Set和Map再Java中都是接口,List主要的实现类是ArrayList和LinkedList,Set的主要实现类是HashSet,Map的主要实现类的HashMap。
比如现在创建一个包含许多水果名称的集合,在java中我们会首先创建一个ArrayList的实例,然后将水果的名称一个个添加到集合中。在kotlin也可以这么做。

fun main(){
val list &#61; ArrayList<String>()
list.add("Apple")
list.add("Banana")
list.add("Orange")
list.add("Pear")
list.add("Grape")
}

但是这种初始化集合的方式比较繁琐&#xff0c;为此Kotlin专门提供了一个内置的listOf()函数来简化初始化集合的写法&#xff0c;如下所示

val list &#61; listOf("Apple", "Banana", "Pear", "Grape")

在循环语句中&#xff0c;for-in循环不仅可以用来遍历区间&#xff0c;还可以用来遍历集合。现在我们就尝试一下使用for-in循环来遍历这个水果集合。

fun main(){
val list &#61; listOf("Apple", "Banana", "Pear", "Grape")
for(fruit in list){
println(fruit)
}
}

在这里插入图片描述
不过需要注意的是&#xff0c;listOf()函数创建的是一个不可变的集合&#xff0c;不可变集合指的就是该集合只能用于读取&#xff0c;我们无法对集合进行添加、修改或删除操作。
当我们需要一个可变的集合&#xff0c;就可以使用mutableListOf()函数&#xff0c;示例如下&#xff1a;

fun main(){
val list &#61; mutableListOf("Apple", "Banana", "Pear", "Grape")
list.add("orange")
for(fruit in list){
println(fruit)
}
}

在这里插入图片描述
Set集合和List集合的用法几乎是一模一样&#xff0c;只是将创建集合的方式换成了setOf()和mutableSetOf()函数而已。

val set &#61; setOf("Apple", "Banana", "Pear", "Grape","orange")
for(fruit in set){
println(fruit)
}

需要注意的是&#xff0c;Set集合底层是使用hash映射机制存放数据的&#xff0c;因此集合中的元素无法保证有序&#xff0c;这是和List集合最大的不同之处。
最后再来看一下Map集合。Map是一种键值对形式的数据结构&#xff0c;Map用法是先创建一个HashMap的实例&#xff0c;然后将一个个键值对数据添加到Map中。比如这里我们给每种水果设置一个对应的编号&#xff0c;可以这样写

val map &#61; HashMap<String, Int>()
map.put("Apple",1)
map.put("Banana",2)
map.put("Orange",3)
map.put("Pear",4)
for(fruit in map){
println(fruit)
}

因为这种写法和Java语法是比较相似的&#xff0c;因此可能比较好理解&#xff0c;但是其实在Kotlin中并不建议使用put()和get()方法来对Map进行添加和读取数据操作&#xff0c;而是推荐使用一种类似于数组下标的语法结构&#xff0c;比如Map中添加一条数据就可以这么写&#xff1a;
map[“Apple”] &#61; 1
而从Map中读取一条数据就可以这么写
val number &#61; map[“Apple”]
经过上面的代码优化后可以这么写

val map &#61; HashMap<String, Int>()
map["Apple"] &#61; 1
map["Banana"] &#61; 2
map["Orange"] &#61; 3
map["Pear"]&#61;4

这还不是最简便的写法&#xff0c;因为Kotlin给我们提供了一对mapOf()和mutableMapOf()函数来继续简化Map的用法。在mapOf()函数中&#xff0c;我们可以直接传入初始化的键值对组合来完成对Map集合的创建&#xff1a;

var map&#61; mapOf("Apple" to 1,"Banana" to 2,"orange" to 3,"Pear" to 4)

这个to不是关键&#xff0c;涉及到了infix函数。

var map&#61; mapOf("Apple" to 1,"Banana" to 2,"orange" to 3,"Pear" to 4)
for((fruit,number) in map ){
println("fruit is $fruit"&#43;" ,number is $number")
}

在for in循环中可以将Map键值对变量一起声明到了一对括号里面&#xff0c;这样当进行循环遍历时&#xff0c;每次遍历的结果就会赋值给这两个键值对变量&#xff0c;最后将他们的值打印出来。
在这里插入图片描述


2.集合的函数式API

在水果集合里面找到单词最长的那个水果&#xff0c;如果不用Lambda表达式&#xff0c;你可能会这么写

val list &#61; listOf("Apple", "Banana", "Pear", "Grape", "orange")
var maxLengthFruit&#61;""
for (fruit in list){
if(fruit.length>maxLengthFruit.length){
maxLengthFruit&#61;fruit
}
}
println("max length Fruit is $maxLengthFruit")

如果我们使用集合的函数式API

val maxLengthFruit &#61; list.maxBy { it.length }
println("max length Fruit is $maxLengthFruit")

这里是借助了Lambda表达式
Lambda表达式的语法结构为&#xff1a;&#xff5b;参数名1&#xff1a;参数类型&#xff0c;参数名2&#xff1a;参数类型->函数体&#xff5d;
首先最外层是一对大括号&#xff0c;如果有参数传入到Lambda表达式的话&#xff0c;我们还需要声明参数列表&#xff0c;参数列表的结尾使用一个->符号&#xff0c;表示参数列表的结束以及函数体的开始&#xff0c;函数体中可以编写任意行代码&#xff0c;并且最后一行代码会自动作为Lambda表达式的返回值
maxBy函数就是一个普通函数&#xff0c;只不过它接收的是一个Lambda类型的参数&#xff0c;并且会在遍历集合时将每次遍历的值作为参数传递给Lambda表达式。maxBy函数的工作原理时将根据我们传入的条件来遍历集合&#xff0c;从而找到该条件下的最大值&#xff0c;比如说想要找到单词最长的水果&#xff0c;那传入的条件就是单词的长度了。
知道了maxBy函数的工作原理后&#xff0c;我们就可以套用刚才的Lambda表达式的语法结构&#xff0c;并将它传入到maxBy函数中

val lambda&#61;{fruit:String ->fruit.length}
val maxLengthFruit&#61;list.maxBy(lambda)//按照lambda表达式的条件进行查找最大长度
println(maxLengthFruit)

可以指导maxBy函数实质上就是接收了一个lambda参数而已&#xff0c;并且这个Lambda参数是完全按照语法结构&#xff1a;参数名&#xff1a;参数类型->函数体来定义的。
这种写法还是可以再进行简化的&#xff0c;可以直接将Lambda表达式传入maxBy函数中

val maxLengthFruit&#61;list.maxBy({fruit:String->fruit.length})

Kotlin规定&#xff0c;当Lambda参数是函数最后一个参数时&#xff0c;可以将Lambda表达式移到函数括号的外面

val maxLengthFruit&#61;list.maxBy(){fruit:String->fruit.length}

接下来&#xff0c;如果Lambda参数是函数的唯一一个参数的话&#xff0c;还可以将函数的括号省略

val maxLengthFruit&#61;list.maxBy{fruit:String->fruit.length}

接下来&#xff0c;还可以继续进行简化&#xff0c;由于Kotlin拥有出色的类型推导机制&#xff0c;Lambda表达式中的参数列表其实在大多数情况下不必要声明参数类型&#xff0c;因此代码进一步简化为

val maxLengthFruit&#61;list.maxBy{fruit->fruit.length}

最后当Lambda表达式的参数列表中只有一个参数时&#xff0c;也不必要声明参数名&#xff0c;而是可以使用it关键字来代替

val maxLengthFruit&#61;list.maxBy{it.length}

通过推导过程&#xff0c;得到了和开始的那一段函数API一模一样的写法。
集合中的map函数也是一种最常用的一种函数API&#xff0c;它用于将集合中的每个元素都映射成一个另外的值&#xff0c;映射规则在Lambda表达式中指定&#xff0c;最终生成一个新的集合。比如这里将所有水果名都变成大写模式&#xff0c;可以这样写

fun main(){
val list &#61; listOf("Apple", "Banana", "Pear", "Grape", "orange")
val newList &#61; list.map { it.toUpperCase() }
for (fruit in newList){
println(fruit)
}
}

可以看到&#xff0c;我们在map函数的Lambda表达式中指定将单词转换成了大写模式&#xff0c;然后遍历这个新的集合。
在这里插入图片描述
map的功能很强大&#xff0c;可以根据我们的需求对集合中的元素进行任意的映射转换&#xff0c;除此之外&#xff0c;还可以将水果名全部转换为小写&#xff0c;或者只取单词的首字母&#xff0c;甚至是转换成单词长度这样的数字集合&#xff0c;只需在Lambda表达式中编写需要的逻辑即可
例如&#xff1a;只取单词首字母

val newList &#61; list.map { it[0]}
for (fruit in newList){
println(fruit)
}

在这里插入图片描述
接下来是另外一个比较常用的函数API–filter函数。顾名思义&#xff0c;filter函数是用来过滤集合中的数据的&#xff0c;它可以单独使用&#xff0c;也可以配合刚才的map函数一起使用。
比如我们只想保留5个字母以内的水果&#xff0c;就可以借助filter函数来实现

fun main(){
val list &#61; listOf("Apple", "Banana", "Pear", "Grape", "orange")
val newList &#61; list.filter { it.length<&#61;5 }.map { it.toUpperCase() }
for (fruit in newList){
println(fruit)
}
}

可以看到同时使用了filter和map函数&#xff0c;并通过Lambda表达式将水果单词长度限制在5个单词以内并将水果名映射为大写字母
在这里插入图片描述
上述代码中&#xff0c;我们是先调用filter函数再调用map函数&#xff0c;如果改成先调用map函数再调用filter函数&#xff0c;效率会差很多&#xff0c;因为你这样相当于先将所有元素映射转换后再进行过滤&#xff0c;这是完全不必要的。先将所有元素过滤后再进行映射转换&#xff0c;这样明显高效许多。

接下来还有两个比较常用的函数式API–any和all函数。其中any函数用于判断集合中是否至少存在一个元素满足指定条件&#xff0c;all函数用于判断集合中是否所有元素满足指定条件

fun main(){
val list &#61; listOf("Apple", "Banana", "Pear", "Grape", "orange")
val anyResult &#61; list.any { it.length <&#61; 5 }
val allResult &#61; list.all { it.length <&#61; 5 }
println("anyResult is "&#43;anyResult&#43;", allResult is "&#43;allResult)
}

此时any函数表示集合中是否存在5个字母以内的单词&#xff0c;而all函数表示集合中是否所有单词都在5个字母以内。
在这里插入图片描述


3.Java函数式API的使用

Kotlin中调用Java方法时也可以使用函数式API&#xff0c;只不过这是有一定条件限制的。具体来讲&#xff0c;如果我们在Kotlin代码中调用了一个Java方法&#xff0c;并且该方法接收一个Java单抽象方法接口参数&#xff0c;就可以使用函数式API&#xff0c;Java单抽象方法接口指的是接口中只有一个待实现方法&#xff0c;如果接口中有多个待实现方法&#xff0c;则无法使用函数式API
例如&#xff1a;Java原生API中有一个最为常见的抽象方法接口–Runnable接口。这个接口中只有一个待实现的run()方法&#xff0c;定义如下&#xff1a;

public interface Runnable{
void run();
}

根据前面的定义&#xff0c;对应任何一个Java方法&#xff0c;只要它接收Runnable参数&#xff0c;就可以使用函数式API。那么什么Java方法接收了Runnable参数呢&#xff1f;这就很多了&#xff0c;不过Runnable接口主要还是结合线程一起使用的。
Thread类的构造方法中接收一个Runnable参数&#xff0c;我们可以使用Java代码创建并执行一个子线程&#xff1a;

new Thread(new Runnable(){
&#64;Override
public void run(){
System.out.println("Thread is running");
}
}).start();

这里使用匿名内部类的写法&#xff0c;我们创建了一个Runnable接口的匿名类实例&#xff0c;并将它给了Thread类的构造方法&#xff0c;最后调用Thread类的start()方法执行这个线程。
如果将这断代码翻译为Kotlin版本&#xff0c;写法如下&#xff1a;

Thread(object:Runnable{
override fun run() {
println("Thread is running")
}
}).start()

Kotlin中匿名内部类的写法和Java有一点区别&#xff0c;由于Kotlin完全舍弃了new关键字&#xff0c;因此创建匿名内部类实例的时候就不能再使用new了&#xff0c;而是改用了object关键字。
还有目前Thread类的构造方法时符合Java函数式API的使用条件的&#xff0c;下面我们就看看如何对代码进行精简

Thread(Runnable { println("Thread is running") }).start()

因为Runnable类中只有一个待实现的方法&#xff0c;即使这里没有显式地重写run()方法&#xff0c;Kotlin也能自动明白Runnable后面的Lambda表达式就是run()方法中要实现的内容。
另外&#xff0c;如果一个Java方法的参数列表中不存在一个以上Java单抽象方法接口参数&#xff0c;我们还可以将接口名进行省略。&#xff08;也就是说Thread方法只需要Runnable一个接口&#xff09;

Thread( { println("Thread is running") }).start()

当Lambda表达式还是方法的最后一个参数时&#xff0c;可以将Lambda表达式移到方法括号的外面。同时&#xff0c;如果Lambda表达式还是方法的唯一一个参数&#xff0c;还可以将方法的括号省略。

Thread{ println("Thread is running") }.start()

在这里插入图片描述
当我们在Kotlin中调用Android SDK接口时&#xff0c;就很有可能会用到这种Java函数式API写法
举个例子&#xff1a;Android中有一个经常用到的点击事件接口OnClickListener&#xff0c;其定义如下&#xff1a;

public interface OnClickListener{
void onClick(View v);
}

可以看到这又是一个单抽象方法接口。假设我们拥有一个按钮button实例&#xff0c;然后使用java代码去注册这个按钮的点击事件

button.setOnClickListener(new View.OnClickListener(){
&#64;Override
public void onClick(View v){
}
});

而用Kotlin代码实现同样的功能&#xff0c;就可以使用函数式API的写法来对代码进行简化&#xff0c;结果如下&#xff1a;

button.setOnClickListener{
}

Java函数式API的使用都限定于从Kotlin中调用Java方法&#xff0c;并且抽象方法接口也必须是用Java语言定义的。
例如&#xff1a;ListView中的点击事件方法setOnItemClickListener接收一个OnItemClickListener接口对象

/**
* Register a callback to be invoked when an item in this AdapterView has
* been clicked.
*
* &#64;param listener The callback that will be invoked.
*/

public void setOnItemClickListener(&#64;Nullable OnItemClickListener listener) {
mOnItemClickListener &#61; listener;
}

OnItemClickListener 接口

public interface OnItemClickListener {
/**
* Callback method to be invoked when an item in this AdapterView has
* been clicked.
*


* Implementers can call getItemAtPosition(position) if they need
* to access the data associated with the selected item.
*
* &#64;param parent The AdapterView where the click happened.
* &#64;param view The view within the AdapterView that was clicked (this
* will be a view provided by the adapter)
* &#64;param position The position of the view in the adapter.
* &#64;param id The row id of the item that was clicked.
*/
void onItemClick(AdapterView<?> parent, View view, int position, long id);
}

因为setOnItemClickListener方法接收一个Java单抽象方法接口参数&#xff0c;就可以使用函数式API&#xff0c;由于onItemClick接口中含有四个参数(一个以上)&#xff0c;使用时简化为

list_view.setOnItemClickListener { parent, view, position, id ->
}

当如果只使用到position这个参数的时候&#xff0c;kotlin允许我们将没有用到的参数使用下划线来替代&#xff0c;因此可以写成下面这种写法

list_view.setOnItemClickListener { _, _, position, _ ->
}






推荐阅读
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • Java程序设计第4周学习总结及注释应用的开发笔记
    本文由编程笔记#小编为大家整理,主要介绍了201521123087《Java程序设计》第4周学习总结相关的知识,包括注释的应用和使用类的注释与方法的注释进行注释的方法,并在Eclipse中查看。摘要内容大约为150字,提供了一定的参考价值。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
author-avatar
初初初初丶初崽崽__冏每_472
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有