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
*/
如果我们想要在集合里找到单词最长的那个水果,我们可以这么写:
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表达式的语法结构,然后就能知道为什么可以这么写了。
正常情况下,我们向某个函数传参时只能传入变量,而借助Lambda却允许传入一小段代码,那么这一小段代码到底是多少呢,其实Kotlin并没有进行限制,但建议不要太长,否则影响可读性。
首先最外层是一对大括号,如果有参数传入到Lambda表达式中的话,我们还需要声明参数列表,参数列表的结尾使用一个->符号,表示参数列表的结束以及函数体的开始,函数体中可以编写任意行代码,并且最后一行代码会自动作为Lambda表达式的返回值。
Lambda表达式的规则:
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}
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函数是用来过滤集合中数据的,它可以单独使用,也可以配合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
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
如果我们在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") }