一.集合

传统意义上的集合是List和Set,再广泛一点的话也要加上Map。其实现类分别为
ArrayList、LinkedList,HsahSet,HashMap

①传统的初始化集合的方式为

fun main() {
val list = ArrayList<String>()
list.add("Apple")
list.add("Banana")
list.add("Orange")
list.add("Pear")
list.add("Grape")

for(fruit in list){
println(fruit)
}
}

②listOf

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

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

③mutableListOf

不过listOf()函数创建的是一个不可变的集合(思想和val关键字,类默认不可继承初衷是类似的),不可变的集合只能用于读取,无法对集合进行添加、修改或删除。如果想让其变成可变的集合,改为**mutableListOf()**即可

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

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

④Map

传统的Map

fun main() {
val map = HashMap<String,Int>()
map.put("Apple",1)
map.put("Banana",2)
map.put("Orange",3)
}

不过kotlin并不建议用put或者get,而是建议使用类似于数组下标的语法结构

fun main() {
val map = HashMap<String,Int>()
map["Apple"] = 1
map["Banana"] = 2
map["Orange"] = 3
}

类似于list和set,map也有mapOf和mutableMapOf,写出来更简单

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

二.lambda表达式

①lambda定义

lambda就是一小段可以作为参数传递的代码,这个很厉害,因为我们一般传递参数的时候都只是传递变量,而借助lambda却允许传入一小段代码(虽然代码数量没有限制,但是不建议传太多,否则会影响代码的可读性)

②lambda表达式的语法结构

{参数名1:参数类型,参数名2:参数类型 -> 函数体}
最外层是一对大括号,参数列表的结尾使用一个->符号,表示参数列表的结束以及函数体的开始,函数体中可以编写任意行代码(当然不建议写太多),并且最后一行代码会自动作为lambda表达式的返回值
实际在应用过程中,我们一般都是用的简化版写法

③lambda表达式的简化过程

(1)原版是这样的

fun main() {
val list = listOf("Apple","Banananana","Orange")
val lambda = { fruit: String ->fruit.length}
val maxLengthFruit = list.maxBy(lambda)

println(maxLengthFruit)
}

(2)首先lambda变量根本无需定义,然后kotlin规定当lambda参数是函数的最后一个参数时,可以将lambda表达式移到函数括号的外面

fun main() {
val list = listOf("Apple","Banananana","Orange")
val maxLengthFruit = list.maxBy(){ fruit: String ->fruit.length}

println(maxLengthFruit)
}

(3)如果lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略

fun main() {
val list = listOf("Apple","Banananana","Orange")
val maxLengthFruit = list.maxBy{ fruit: String ->fruit.length}

println(maxLengthFruit)
}

(4)最后,由于kotlin拥有出色的类型推导机制,所以lambda表达式中的参数列表其实在大多数情况下不必声明参数类型

fun main() {
val list = listOf("Apple","Banananana","Orange")
val maxLengthFruit = list.maxBy{ fruit ->fruit.length}

println(maxLengthFruit)
}

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

fun main() {
val list = listOf("Apple","Banananana","Orange")
val maxLengthFruit = list.maxBy{ it.length}

println(maxLengthFruit)
}

这就是简化过程的完整版了

三.集合中几个比较常用的函数式API

①map函数

集合中的map函数是最常用的一种函数式API,它用于将集合中的每个元素都映射成一个另外的值,比如将所有的水果都变成大写

fun main() {
val list = listOf("Apple","Banananana","Orange")
val list1 = list.map{ it.toUpperCase()}
for(newFruit in list1){
println(newFruit)
}

}

②filter函数

是用来过滤集合中的数据的。它既可以单独使用,也可以配合刚才的map函数一起使用

fun main() {
val list = listOf("Apple","Banananana","Orange")
val list1 = list.filter { it.length<= 6 }.map{ it.toUpperCase()}

for(fruit in list1){
println(fruit)
}
}
/*
APPLE
ORANGE

*/

这里就是将字母长度小于等于6的水果转化为大写,注意,虽然这里filter和map函数互换位置后仍可以实现,但是效率会降低,因为先用map,会先遍历所有的元素。

③any和all

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

fun main() {
val list = listOf("Apple","Banananana","Orange")
val result1 = list.any { it.length <= 5 }
val result2 = list.all { it.length <= 5 }
println(result1)
println(result2)

}
/*
true
false
*/

④Java函数式API的使用

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

比如Runnable接口

interface Runnable{
void run();
}

按照前面说的,对于任何一个Java方法,只要它接收Runnable参数,就可以使用函数式API
Java版本

public static void main(String[] args) {
new Thread(new Runnable(){
@Override
public void run() {
System.out.println("run");
}
}).start();
}

kotlin版本(未简化的)

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

简化后,就可以变成

fun main() {
Thread{
println("run")
}.start()
}

我理解为省略了接口名和接口的唯一待实现函数名,直接写函数体

四.空指针检查

①可空类型系统

kotlin默认所有的参数和变量都不可为空,将空指针异常的检查提前到了编译时期。
可为空的类型系统就是在类名的后面加上一个问号,比如Int? String?

class Study(){
fun doLearn(){}
fun doRead(){}
}

fun main() {
doStudy(null)
}

fun doStudy(study: Study?){
if(study != null) {
study.doLearn()
study.doRead()
}
}

②判空辅助工具

(1)?.

当对象不为空时正常调用相应的方法,当对象为空时则什么都不做

比如

if(a != null){
a.doSomething()
}

可以简化为

a?.doSomething()

(2)?:

这个操作符的左右两边都接收一个表达式,如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果

比如

val a = if(study != null){
study
}else{
0
}

可以改为

val a = study ?: 0

(3)!!

!!是非空断言工具,意在表示这里的对象一定不为空,这是一种有风险的写法

(4)let函数

这个函数提供了函数式API的编程接口,并将原始调用对象作为参数传递到Lambda表达式中

fun doStudy(study: Study?){
study?.doLearn()
study?.doRead()
}

上面这种写法是比较啰嗦的,如果使用let,就会简化

fun doStudy(study: Study?){
study?.let {
it.doRead()
it.doLearn()
}
}