Kotlin中的Lambda编程

news2024/11/20 12:23:03

文章目录

      • 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 = ArrayList<String>()
    list.add("Apple")
    list.add("Banana")
    list.add("Orange")
    list.add("Pear")
    list.add("Grape")
}

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

  val list = listOf("Apple", "Banana", "Pear", "Grape")

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

fun main(){
    val list = listOf("Apple", "Banana", "Pear", "Grape")
    for(fruit in list){
        println(fruit)
    }
}

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

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

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

    val set = setOf("Apple", "Banana", "Pear", "Grape","orange")
    for(fruit in set){
        println(fruit)
    }

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

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

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

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

    var map= mapOf("Apple" to 1,"Banana" to 2,"orange" to 3,"Pear" to 4)

这个to不是关键,涉及到了infix函数。

    var map= mapOf("Apple" to 1,"Banana" to 2,"orange" to 3,"Pear" to 4)
    for((fruit,number) in map ){
        println("fruit is $fruit"+" ,number is $number")
    }

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

2.集合的函数式API

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

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

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

    val maxLengthFruit = list.maxBy { it.length }
    println("max length Fruit is $maxLengthFruit")

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

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

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

    val maxLengthFruit=list.maxBy({fruit:String->fruit.length})

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

   val maxLengthFruit=list.maxBy(){fruit:String->fruit.length}

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

   val maxLengthFruit=list.maxBy{fruit:String->fruit.length}

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

    val maxLengthFruit=list.maxBy{fruit->fruit.length}

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

    val maxLengthFruit=list.maxBy{it.length}

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

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

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

    val newList = list.map { it[0]}
    for (fruit in newList){
        println(fruit)
    }

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

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

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

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

fun main(){
    val list = listOf("Apple", "Banana", "Pear", "Grape", "orange")
    val anyResult = list.any { it.length <= 5 }
    val allResult = list.all { it.length <= 5 }
    println("anyResult is "+anyResult+", allResult is "+allResult)
}

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

3.Java函数式API的使用

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

public interface Runnable{
void run();
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

button.setOnClickListener{

}

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

    /**
     * Register a callback to be invoked when an item in this AdapterView has
     * been clicked.
     *
     * @param listener The callback that will be invoked.
     */
    public void setOnItemClickListener(@Nullable OnItemClickListener listener) {
        mOnItemClickListener = listener;
    }

OnItemClickListener 接口

  public interface OnItemClickListener {

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

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

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

        }

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

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

        }

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

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

相关文章

Java设计模式-命令模式Command

介绍 命令模式&#xff08;Command Pattern&#xff09;&#xff1a;在软件设计中&#xff0c;我们经常需要向某些对象发送请求&#xff0c;但是并不知道请求的接收 者是谁&#xff0c;也不知道被请求的操作是哪个&#xff0c; 我们只需在程序运行时指定具体的请求接收者即可&…

数影周报:TikTok因在线跟踪被罚500万欧,Windows 7退出历史舞台

本周看点&#xff1a;TikTok因在线跟踪被法国罚款500万欧元 &#xff1b;思科已裁员近700 人&#xff1b;Windows 7退出历史舞台&#xff1b;亚马逊向所有卖家开放Buy with Prime服务&#xff1b;“全路程”完成2亿元C轮融资......数据安全那些事TikTok因在线跟踪被法国罚款500…

Android13 wifi无线调试adb连接设置

在进行adb调试的时候&#xff0c;有时候需要使用wifi连接&#xff0c;或者wifi连接较为方便&#xff0c;早些的Android上&#xff0c;需要设置端口等操作&#xff0c;adb tcpip 6666参考android wifi adb 调试 - 简书 (jianshu.com)好几步操作&#xff0c;在Android13上&#x…

Deque 的理解 STL中stack与queue为什么选择使用deque为底层模板容器

目录 一、Deque的引入 二、Deque是什么&#xff1f; 三、deque的遍历方式&#xff1f;deque的缺陷&#xff1f; 四、它为什么能更贴合与stack与queue&#xff1f; 五、STL中vector与list的底层实现 一、Deque的引入 Stack、Queue在之前的博客中我也是分别使用了更容易处理…

【蓝桥杯】历届真题 杨辉三角形 (省赛)Java

【问题描述】 下面的图形是著名的杨辉三角形: 如果我们按从上到下、从左到右的顺序把所有数排成一列&#xff0c;可以得到如下数列: 1,1&#xff0c;1&#xff0c;1&#xff0c;2,1&#xff0c;1&#xff0c;3,3&#xff0c;1&#xff0c;1,4&#xff0c;6,4&#xff0c;1&…

数字IC设计、验证、FPGA笔试必会 - Verilog经典习题 (六)多功能数据处理器

数字IC设计、验证、FPGA笔试必会 - Verilog经典习题 &#xff08;六&#xff09;多功能数据处理器 &#x1f508;声明&#xff1a; &#x1f603;博主主页&#xff1a;王_嘻嘻的CSDN博客 &#x1f9e8;未经作者允许&#xff0c;禁止转载 &#x1f511;系列专栏&#xff1a;牛客…

react基础Day02-受控组件评论案例propscontext

React组件 目标 能够知道受控组件是什么能够写出受控组件了解非受控组件 表单处理 受控组件&#xff08;★★★&#xff09; HTML中的表单元素是可输入的&#xff0c;也就是有自己的可变状态而React中可变状态通常保存在state中&#xff0c;并且只能通过setState() 方法来…

[acwing周赛复盘] 第 86 场周赛20230114

[acwing周赛复盘] 第 86 场周赛20230114 一、本周周赛总结二、 4794. 健身1. 题目描述2. 思路分析3. 代码实现三、4795. 安全区域1. 题目描述2. 思路分析3. 代码实现四、4796. 删除序列1. 题目描述2. 思路分析3. 代码实现六、参考链接一、本周周赛总结 去吃羊蝎子了&#xff0…

基于汽车知识图谱的汽车问答多轮对话系统 详细教程

结果: 1 技术路线 该技术路线主要将KBQA分为三部分,实体识别与实体链接,关系识别,sparql查询,其中每个部分分为一到多种方法实现。具体的处理流程图如下:

大脑的记忆

AI神经网络中的记忆 当前AI发展进入一个瓶颈,大家都意识到还是要继续在人脑中获取AI方向的指引。当然也有科学家说物理世界与心理世界并非一一对应,人类的智能也没必要与物理世界一一对应,甚至本质上都可以是不同的,所以没必要研究大脑认知和大脑的机制,更不需要分子级别…

IDEA structure窗口各标志及功能

文章目录图标对象类型访问权限其他修饰符工具栏图标 对象类型 class 类 interface 接口 enum 枚举 interface 注解 class initializer 代码块 method 方法 field 字段/属性 anonymous class 匿名类 lambda lambda表达式 propertie 访问器&#xff08;get方法&#xff0…

【Java面试】Queue接口

文章目录BlockingQueue中有哪些方法&#xff0c;为什么这样设计&#xff1f;BlockingQueue是怎么实现的&#xff1f;BlockingQueue中有哪些方法&#xff0c;为什么这样设计&#xff1f; 先看一眼结构&#xff0c;再看具体的分析 为了应对不同的业务场景&#xff0c;Blockin…

拉伯证券|业绩猛增超13倍,主力连续抢筹,这只股收获4连板

成绩陡增股获主力接连抢筹 春节日益接近&#xff0c;A股成交活跃度有所下滑&#xff0c;不过有一些股票节前继续取得主力喜爱。证券时报•数据宝核算&#xff0c;到1月12日收盘&#xff0c;沪深两市共54只个股接连5日或5日以上主力资金净流入。 主力资金净流入继续周期最长的是…

人工智能学习07--pytorch03--tensorboard(下载tensorboard、opencv)

transform 主要是对input图像进行变换&#xff08;统一尺寸、对图像中的数据进行类的转换&#xff09; TensorBoard很有用 如&#xff1a;通过loss的变化过程&#xff0c;来看loss的变化是否复合预想。也可以通过loss来选择模型。 TensorBoard&#xff0c;虽然他是TensorFlo…

排序综合(C++版)

目录 排序综合 一、问题描述 二、运行环境说明 三、代码段 四、效果展示 排序综合 备注&#xff1a;大二&#xff08;上&#xff09;数据结构课程设计B题 一、问题描述 给定N…

Python asyncio异步编程简单实现

今天继续给大家介绍Python相关知识&#xff0c;本文主要内容是Python asyncio异步编程简单实现。 一、asyncio事件循环简介 asyncio引入了事件循环的概念。事件循环是一个死循环&#xff0c;还循环会检测并执行某些代码。在Python中&#xff0c;引入了asyncio模块后&#xff…

动态内存管理:学习笔记9

目录 一.前言 二.动态内存函数 1.malloc和free 2.calloc函数 3. realloc函数(动态内存空间调整函数) 情形一&#xff1a;扩容时&#xff0c;原内存地址处可以容纳调整后的动态内存 情形二&#xff1a;扩容时&#xff0c;原内存地址无法容纳调整后的动态内存 三.C/C程序…

MATLAB实现费诺编码的计算与分析

一、实验目的 1、理解霍费诺编码的原理。 2、掌握费诺编码的方法和步骤。 3、熟悉费诺编码的效率。 4、本实验用Matlab语言编程实现费诺&#xff08;Fano&#xff09;编码。 二、实验环境 windows XP&#xff0c;MATLAB 7 三、实验原理 费诺编码算法如下&#xff1a;在信源…

构建前端项目

1.使用vite构建vue项目 vite构建vue项目&#xff0c;输入以下命令&#xff1a; npm init vitelatest接着按照提示的命令选择项目的名称、框架、语言。接着项目就构建完成了。 接着将构建好的项目&#xff1a;vite-demo拖入vsCode里面&#xff0c;在package.json中可以看到项…

redis基础命令使用

目录 Redis redis存储结构&#xff08;KV&#xff09; String string类型介绍 string类型数据的基础操作 string类型数据的扩展操作 List list类型介绍 list类型数据基本操作 list类型数据扩展操作 hash hash类型介绍 hash类型数据的基本操作 hash类型数据扩展操…