Kotlin: Lambda编程(补档)

List集合

由于逐项初始化集合的方式比较繁琐,kotlin专门提供了一个内置的listOf()函数用来简化初始化集合的过程,如下代码:

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

尝试用 for-in 循环来遍历这个水果集合:

1
2
3
4
5
6
fun main() {
val list = listOf("Apple" ,"Banana" , "Orange" , "Pear" , "Grape")
for (fruit in list) {
println(fruit)
}
}

注意:listOf定义的集合是一个不可变的集合(只读),如果要创建一个可变的集合,需用关键字mutableListOf

1
val list = mutableListOf("Apple" , "Banana" , "Orange" , "Pear" , "Grape")

修改可变的list集合:

1
2
3
4
5
6
7
fun main() {
val list = mutableListOf("Apple" ,"Banana" , "Orange" , "Pear" , "Grape")
list.add("watermelon")
for (fruit in list) {
println(fruit)
}
}

Set集合

Set集合的用法与list集几乎一模一样,只是将定义集合的方式换成了setOf()和mutableSetOf()函数而已,大致代码如下

1
2
3
4
5
6
7
fun main() {
val set = mutableSetOf("Apple" ,"Banana" , "Orange" , "Pear" , "Grape")
set.add("watermelon")
for (fruit in set) {
println(fruit)
}
}

需要注意:Set集合底层是使用hash映射机制来存放数据的,因此集合中的元素无法保证顺序。这是Set集合和List集合最大的不同之处。


Map集合

Map集合是一种键值对形式的数据结构,因此在用法上和list、Set有比较大的不同。传统的Map用法是先创建一个HashMap的实例

,然后将一个个键值对数据添加到Map中。如下代码:

1
2
3
4
5
6
val map = HashMap<String , Int> ()
map.put("Apple",1)
map.put("Banana",2)
map.put("Orange",3)
map.put("Pear",4)
map.put("Grape",5)

以上的写法比较接近Java中的语法,也比较好理解。但在Kotlin中推荐使用一种类似于数组下标的语法结构,如下代码:

1
map["Apple"]=1

而从Map中读取一条数据就可以这样写:

1
val number = map["Apple"]

因此,上述代码经过优化后就可以变成如下形式:

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

当然,这仍然不是最简便的写法,在Kotlin中,可以写如下代码:

1
val map = mapOf("Apple" to 1 , "Banana" to 2 , "Orange" to 3 , "Pear" to 4 , "Grape" to 5)

注:这里用于关联键值对的“to"不是一个关键字,而是一个infix函数。

Map集合中的数据也可以使用 for-in 循环进行遍历,如下代码:

1
2
3
4
5
6
fun main(){
val map = mapOf("Apple" to 1 , "Banana" to 2 , "Orange" to 3 , "Pear" to 4 , "Grape" to 5)
for((fruit , number) in map) {
println("fruit is" + fruit + " , number is " + number)
}
}

集合的函数式API

Lamda的定义

Lambda就是一小段可以作为参数传递的代码。【Kotlin中不对“一小段代码”究竟有多小进行限制,但是不建议在Lambda表达式中编写太长的代码,否则可能会影响代码的可读性】

Lambda表达式的语法结构:

{参数名1 : 参数类型 , 参数名2 : 参数类型 -> 函数体}

这是Lambda表达式最完整的语法结构定义。

  • 最外层是一对大括号
  • 如果有参数传入到Lambda表达式中,我们还需要声明参数列表
  • 参数列表的结尾用一个 -> 符号,表示参数列表的结束以及函数体的开始
  • 函数体中可以编写任意行代码,最后一行会自动作为Lambda表达式的返回值

在很多情况下,我们无需使用Lambda表达式的完整语法结构,而是有很多简化的写法,以下为一步步简化的过程

  • 最基本的Lambda写法:
1
2
3
val list = listOf("Apple" , "Banana" , "Orange" , "Pear" , "Grape" , "Watermelon")
val lambda = {fruit : String -> fruit.length }
val maxLengthFruit = list.maxBy(lambda)

代码分析:1.fruit : String : 向 lambda中传入了参量 fruit,这是最后一个参量,因此lambda默认返回值即为 fruit

代码分析:2.fruit.length:这部分是我们定义的lambda的函数体,maxBy函数的工作原理是按我们传入的条件来遍历集合,从而找到该条件下的最大值。我们希望找到名字最长的fruit,因此传入了fruit.length

  • 简化:我们并不需要专门定义一个lambda变量,而是可以直接将lambda表达式传入maxBy函数当中:
1
val maxLengthFruit = list.maxBy({fruit : String -> fruit.length })
  • 简化:Kotlin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号的外面:
1
val maxLengthFruit = list.maxBy(){fruit : String -> fruit.length }
  • 简化:如果Lambda参数是函式的唯一一个参数时,可以将函数的括号省略:
1
val maxLengthFruit = list.maxBy{fruit : String -> fruit.length }
  • 简化:由于Kotlin拥有出色的类型推导机制,Lambda表达式中的参数列表其实在大多情况下不必声明参数类型:
1
val maxLengthFruit = list.maxBy{fruit -> fruit.length }
  • 简化:当Lambda表达式的参数列表中只有一个参数时,不必声明参数名,可以直接用it关键字来替代:
1
val maxLengthFruit = list.maxBy{it.length }

因此,原程序代码可以如下编制:

1
2
val list = listOf("Apple" , "Banana" , "Orange" , "Pear" , "Grape" , "Watermelon")
val lambda = list.maxBy {it.length}

集合中常用的函数式API

map函数

map函数是最常用的一种函数式API,它用于将集合中的每个元素都映射成一个另外的值,映射的规则在Lambda表达式中指定,最终生成一个新的集合。例如,我们希望让所有的水果名都变成大写模式,代码如下:

1
2
3
4
fun mian () {
val list = listOf("Apple" , "Banana" , "Orange" , "Pear" , "Grape" , "Watermelon")
val newlist = list.map{ it.toUpperCase() }
}

map函数的功能非常强大,它可以按照我们的需求对集合中的元素进行任意的映射转换,只要在Lambda表达式中编写所需要的逻辑即可。


filter函数

filter函数用于过滤集合中的数据,它可以单独使用,也可以同map函数一起使用

例如,我们只想保留5个字母以内的水果,就可以借助filter函数来实现,代码如下:

1
2
3
4
5
fun main() {
val list = listOf("Apple" , "Banana" , "Orange" , "Pear" , "Grape" , "Watermelon")
val newList = list.filter { it.length <= 5 }
.map { it.toUpperCase() }
}

any 和 all 函数

  • any函数用于判断集合中是否至少存在一个元素满足指定条件
  • all用于判断集合中是否所有元素都满足判定条件

示例代码如下:

1
2
3
4
5
fun main() {
val list = listOf("Apple" , "Banana" , "Orange" , "Pear" , "Grape" , "Watermelon")
val anyResult = list.any { it.length <= 5 }
val allResult = list.all { it.length <= 5 }
}

传回给anyResult的值是Boolean型


JAVA函数式API的使用

Kotlin调用Java方法时也可以使用函数式API,但是有一定限制。如果我们在Kotlin代码中调用了一个Java方法,并且该方法接受一个Java单抽象方法接口参数,就可以使用函数式API。

具体内容,参考《第一行代码 Android》(第三版)P69~P70 :2.6.3 Java函数式API的使用