目录

  • 1. 集合
  • 1.1 集合的创建和遍历
  • 1.2 集合函数式API使用
  • 2. Lambda编程
  • 2.1 Lambda详解
  • 2.2 集合常用的函数式API
  • map()
  • filter()
  • any() 和 all() 函数
  • 2.3 Java 常用的函数式API


1. 集合

1.1 集合的创建和遍历

Kotlin中专门提供了内置函数来初始化集合
使用如下代码可以初始化一个List集合:

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

但我们需要注意的是,listOf()创建的是一个不可变的集合,只能用于读取,无法进行增,删,改的操作。我们可以使用mutableListOf()来创建一个可变集合:

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

我们可以使用for-in来遍历集合:

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

Set和List的用法几乎一致,使用如下代码可以初始化一个Set集合:

val set = setOf("Apple", "Banana", "Orange", "Pear", "Grape")
val set = mutableSetOf("Apple", "Banana", "Orange", "Pear", "Grape")

Map的初始化和Java比较类似,可以使用如下代码可以初始化一个Map集合:

val map = HashMap<String,Int>()
    map.put("Apple",1)
    map.put("banana",2)
    map.put("Orange",3)

我们可以使用put()和get()方法对map进行添加和读取操作,但是Kotlin有更加推荐的写法:

//添加
map["Apple"] = 1
//读取
val number = map["Apple"]

但这仍然不是最简便的写法,Kotlin提供了mapOf()和mutableMapOf()方法来初始化Map:

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

这里的to并不是一个关键字,而是一个infix函数( Kotlin-语法A to B,infix函数(第一行代码Kotlin学习笔记10)),我们仍然可以使用for-in来遍历map:

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)
    }
}
 /*
    fruit is Apple, number is 1
    fruit is Banana, number is 2
    fruit is Orange, number is 3
    fruit is Pear, number is 4
    fruit is Grape, number is 5
 */

1.2 集合函数式API使用

如果我们想要在集合里找到单词最长的那个水果,我们可以这么写:

fun main() {
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
    var maxLengthFruit = ""
    for (fruit in list){
        if(fruit.length > maxLengthFruit.length){
            maxLengthFruit = fruit
        }
    }
    println("max length fruit is ${maxLengthFruit}")
}
//max length fruit is Watermelon

但是如果我们使用函数式API,就可以让这段代码更加简洁:

fun main() {
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
    var maxLengthFruit = list.maxBy { it.length }
    println("max length fruit is ${maxLengthFruit}")
}
//max length fruit is Watermelon

如果这段代码让我们摸不着头脑,这里的maxBy()就是一个普通函数,它接收一个lambda类型的参数,并会在遍历集合时将每次遍历的值作为参数传递给Lambda表达式,maxBy()的工作原理是根据我们传入的条件来遍历集合,从而找到该条件下的最大值。接下来我们就学习下Lambda表达式的语法结构,然后就能知道为什么可以这么写了。

2. Lambda编程

2.1 Lambda详解

  • 定义:Lambda是一小段可以作为参数传递的代码。
  • 语法:{ 参数名1 : 参数类型, 参数名2 : 参数类型 -> 函数体 }

正常情况下,我们向某个函数传参时只能传入变量,而借助Lambda却允许传入一小段代码,那么这一小段代码到底是多少呢,其实Kotlin并没有进行限制,但建议不要太长,否则影响可读性。
首先最外层是一对大括号,如果有参数传入到Lambda表达式中的话,我们还需要声明参数列表,参数列表的结尾使用一个->符号,表示参数列表的结束以及函数体的开始,函数体中可以编写任意行代码,并且最后一行代码会自动作为Lambda表达式的返回值。
Lambda表达式的规则:

  • 当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号的外面。
  • 如果Lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略。
  • 由于Kotlin拥有出色的类型推导机制,Lambda表达式中的参数列表其实在大多数情况下也不必声明参数类型。
  • 当Lambda表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it关键字来代替。
    下面我们来跟着代码具体解释下各条规则,首先,我们的代码应该是这样的:
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
 val lambda = {fruit : String -> fruit.length}
 val maxLengthFruit = list.maxBy(lambda)

我们不使用变量,而是将Lambda整体放在参数位置,进一步简化为:

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

当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}

2.2 集合常用的函数式API

map()

map函数是最常用的一种函数式API,它用于将集合中的每个元素都映射成一个另外的值,映射的规则则在lambda表达式中指定,最终形成一个新的集合。

fun main() {
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
    val newList = list.map { it.toUpperCase() }
    for (fruit in newList){
        print("${fruit},")
    }
}
//APPLE BANANA ORANGE PEAR GRAPE WATERMELON

当然我们可以实现更复杂的功能,只需要在Lambda表达式中编写我们的逻辑即可。

filter()

顾名思义,filter函数是用来过滤集合中数据的,它可以单独使用,也可以配合map函数一起使用,比如我们想保留5个字母以内的水果:

fun main() {
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
    val newList = list.filter { it.length <= 5 }.map { it.toUpperCase() }
    for (fruit in newList){
        print("${fruit} ")
    }
}
//APPLE PEAR GRAPE

any() 和 all() 函数

  • any函数用于判断集合中是否至少存在一个元素满足指定条件
  • all函数用于判断集合中是否所有元素都满足指定条件
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 }
    println("anyResult is ${anyResult}, allResult is ${allResult}")
}
//anyResult is true, allResult is false

2.3 Java 常用的函数式API

如果我们在Kotlin代码中调用了一个Java方法,并且该方法接收一个Java单抽象方法接口参数,就可以使用函数式API。Java单抽象方法接口指的是接口中只有一个待实现方法,如果接口中有多个待实现方法,则无法使用函数式API。
例如我们常用的接口:Runnable

public interface Runnable{
	void run();
}

在Java中我们是这么用的:

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

如果将代码翻译为Kotlin版本,是这样婶儿的:

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

由于Kotlin完全舍弃了new关键字,因此匿名类实例的时候就不能用new了,而改用object 关键字。并且此段代码符合Java函数式API的使用条件,因此我们可以对代码进行简化:

Thread(Runnable{
    println("thread run")
}).start()

因为Runnable中只有一个待实现方法,即使这里没有显示的重写run()方法,Kotlin也编译器也能明白Runnable后面的Lambda表达式就是要在run()方法中实现的内容,另外,如果一个Java方法的参数列表中有且只有一个Java抽象方法接口参数,我们可以将接口名进行省略,这样代码可进一步简化为:

Thread({
    println("thread run")
}).start()

这样继续遵循Lambda表达式规则,将Lambda表达式移到方法括号的外边,同时,表达式是方法唯一的一个参数,还可以将方法的括号省略,最终代码简化为:

Thread{
    println("thread run")
}.start()

这样,我们熟悉的Android点击事件回调就可以这么写了:

button.setOnclickListener{ println("click button") }