内容
- Lambda表达式语法
- Lambda表达式的优化
- Lambda的作用域
- Lambda在集合中常用的操作
- MaxBy最大值函数,filter过滤集合,map转换一个新集合,forEach循环集合
- 集合判断式 all ,any ,count ,find
- list分组成map
- 嵌套集合元素处理flatMap
- 集合惰性操作
一Lambda表达式
1.1简介
java在jdk1.8的时候引入了Lambda表达式的支持,我记得Lambda表达式并不属于任何语言特有的,类似像正则表达式属于谁一样。他的本质是将一段代码当成值到处传递。依稀记得依赖第三方去使用Lambda表达式去写代码。java中的lambda和kotlin中的写法还有细微的区别。和java写法差别:
java中:
findViewById(R.id.tv).setOnClickListener(view-> {});
复制代码
Kotlin中:
tv.setOnClickListener { view-> }复制代码
1.2Lambda表达式的语法
它的本质是将一段代码当成值到处传递,可以被独立的声明并储存到变量中,但是一般用法还是声明它并传递给函数。
{ x: Int, y: Int -> x + y }复制代码
语法:最外层始终有花括号包裹,箭头左边参数没有用小括号括起来,箭头右边是方法体
如果你愿意,可以用一个变量引用着
var lam = { x: Int, y: Int -> x + y }复制代码
二Lambda和集合
2.1.MaxBy函数
MaxBy函数可用在任何集合中,作用:求集合中的最大值并返回,用法:
2.1.1集合中储存自定义对象:
val arr1 = listOf<Person>(Person(name = "张三", age = 15), Person(name = "李四", age = 30))
//按照表达式语法进行书写,并当成值传递
arr1.maxBy({ p: Person -> p.age }) 复制代码
优化这段代码的语法让他更简介:
优化1:如果lambda表达式是函数调用的最后一个实参,它可以放到括号的外边。
arr1.maxBy(){ p: Person -> p.age }复制代码
arr1.maxBy{ p: Person -> p.age }复制代码
优化3:如果lambda参数的类型可以被推导出来,你就不需要显式地指定它。
arr1.maxBy { p -> p.age }复制代码
优化4:如果只有一个参数的lambda且这个参数的类型可以推断出来,就会生成参数it。
arr1.maxBy {it.age }复制代码
2.2forEach
作用:在集合中的每一个元素上都调用给定的lambda。
val errors = listOf<String>("403 sdfsdfsdf", "404 not dfudsfsd")复制代码
可以有一下写法:
errors.forEach({error:String->Log.e("rrrrr",error)})
errors.forEach(){error:String->Log.e("rrrrr",error)}
errors.forEach{error:String->Log.e("rrrrr",error)}
errors.forEach{error->Log.e("rrrrr",error)}
errors.forEach{Log.e("rrrrr",it)}复制代码
2.3Lambda的作用域
属性,方法内局部变量 都可以是使用和改变,但是java中方法内局部变量只能使用,但不能改变。
2.4filter
作用:遍历集合过滤掉让表达式返回false的元素,留下返回true的元素。原集合不变。
val index = arrayListOf<Int>(1, 2, 3, 4)
val newindex = index.filter { it % 2 == 0 }
Log.e("rrrrrr",newindex.toString())复制代码
上面的结果是一个新的集合,它只包含输入集合中那些满足判断式的元素。
2.5map
作用:从老集合中取出元素,加上逻辑生成一个新的集合。
val index = arrayListOf<Int>(1, 2, 3, 4)
Log.e("rrrrrrrrrr",index.map { it*it }.toString())复制代码
打印结果如下
[1, 4, 9, 16]
2.6 map集合的过滤和转换
val map = mapOf<String, String>("1" to "One", "2" to "Two")
val eee = map.filterKeys { it == "1" }.mapKeys { (it.key.toInt() * 10).toString() }
Log.e("rrrrrrr", eee.toString())复制代码
打印结果:{10=One} 这里返回的还是map集合
三集合判断式“all”“any”“count”和“find”
3.1 all
作用:判断集合中是否全部元素都满足你传入的条件,返回boolean值
val people = listOf(Person(" Alice", 18), Person("Bob", 31))
val all = people.all { it.age > 18 }复制代码
这里返回true。
3.2any
作用:判断集合中是否有满足条件的元素,返回boolean值
val people = listOf(Person(" Alice", 18), Person("Bob", 31))
val any = people.any { it.age > 32 }复制代码
这里返回false。
3.3count
作用:获取满足条件的元素的个数,返回Int
val people = listOf(Person(" Alice", 18), Person("Bob", 31))
val count = people.count { it.age > 20 }
复制代码
这里返回1
3.4find
作用:返回满足条件的第一个元素,返回集合中的元素类型
val people = listOf(Person(" Alice", 18), Person("Bob", 31))
val find = people.find { it.age > 20 }复制代码
这里返回 :Person(name=Bob, age=31)
四 list分组成map
作用:把符合条件的分成一组,不符合条件的分成一组,返回Map<Boolean,List<Person>>
val people = listOf(Person(" Alice", 18), Person("Bob", 30), Person("LiLei", 31))
val maps = people.groupBy { it.age > 18 }复制代码
打印结果:
{false=[Person(name= Alice, age=18)], true=[Person(name=Bob, age=30), Person(name=LiLei, age=31)]}复制代码
五嵌套集合的处理
5.1flatMap
作用,获取集合中嵌套的集合合并成一个集合
val books = listOf<Book>(Book("成功上位", arrayListOf("张三,李四")), Book("成功上位2", arrayListOf("王五", "赵六")))
val eee = books.flatMap { it.auther }复制代码
打印结果:[张三,李四, 王五, 赵六]
注意:这个和rxjava完全不同,不要混为一谈。如果这里用books.map { it.auther }返回的就是:[[张三,李四], [王五, 赵六]]
flatMap干了两件事:1干了map的事情。2把map中的多个集合合并成一个集合。
如果不想重复可以调用toset方法去重。
注意:链式集合函数调用这些函数会及早地创建中间集合,也就是说每一步的中间结果都被存储在一个临时列表。而非像rxjava那样流式调用。如果是大量数据进行操作,就会出现效率地下的问题,为此Kotlin工程师还给我们提供了流式调用。
六 序列:也叫惰性操作集合
解决的问题:中间不去创建集合,提高效率。
6.1需要先把集合转换成序列,操作之后,再把序列转换成集合
index.asSequence()
.filter {
Log.e("rrrrrr1",""+it)
it%2==0 }.map {
Log.e("rrrrrr2",""+it*it)
it*it }
.toList()复制代码
这个时候就不会每次都去创建一个新的集合。就拿上边来说,执行filter被记录下来,延时计算,再执行map还是记录下来,延时计算,直到调用tolist的,才会去触发所有的延期计算。
6.2自己创建序列
前边的例子都是用集合转换成序列,这里我们说一下怎样自己创建序列。
例如:我们去计算100以内的自然数之和,代码可以这样写:
val sss = generateSequence(0, { it + 1 })
val www = sss.takeWhile { it <= 100 }
www.sum()复制代码
注意:这个例子中的sss和www都是有延期操作的序列。这些序列中的实际数字直到你调用末端操作(这里是sum)的时候才会求值。
七lambda传递给java方法
7.1函数式接口定义
当java中定义的接口里边只有一个抽象方法,我们就叫这样的接口为函数式接口。Kotlin允许你在调用接收函数式接口作为参数的方法时使用lambda。如android的点击事件的回调。
7.2如果一个方返回函数式接口,我们不能直接返回一个表达式:需要用构造方法包裹起来像这样:
错误:
fun getRunnable():Runnable{
return { }
}复制代码
需要这样包裹:
fun getRunnable():Runnable{
return Runnable { }
}复制代码
8.1定义
在lambda函数体内可以调用一个不同对象的方法,而无需显示的声明这个对象的引用的lambda叫作带接收者的lambda。
说的什么鬼话?别着急!往下看就懂了
继续往下看,假如我们有要打印A到Z的字符串的需求,我们大概会这样写:
fun getAToZ(): String {
val stringbuffer = StringBuffer()
for (c in 'a'..'z') {
stringbuffer.append(c)
}
return stringbuffer.toString()
}复制代码
这里我们是不是在方法中使用了StringBuffer对象的方法,而且显示的声明了stringbuffer,而我们不要方法中出现这个stringbuffer引用的变量,并且修改为lambda的形式,这种就叫做带接收者的lambda。
知道了需求,我们怎么做呢?
Kotlin给我们提供了一个with的函数,来做这个事情,我们先用with去修改,然后再解释代码:
fun getAToZ(): String {
val stringbuffer = StringBuffer()
return with(stringbuffer) {
for (c in 'a'..'z') {
this.append(c)
}
this.toString()
}
}复制代码
这里的this就是代表着stringbuffer,还记的扩展函数的this指的是扩展哪个类型的事例吗?这里指定的是传递过来的对象,也就是外部对象。
还是有stringbuffer,继续改进。。。
fun getAToZ(): String = with(StringBuffer()) {
for (c in 'a'..'z') {
this.append(c)
}
this.toString()
}复制代码
这是只是把代码块体变成表达式体,就完全消除了变量,让代码变得更加有可读性。注意这里的this可以省略。
假如想调用类中的方法,可以像这样修改,同时解决了方法名字一样导致的冲突
fun getAToZ(): String = with(StringBuffer()) {
for (c in 'a'..'z') {
this.append(c)
}
this@MainActivity.toString()
}
override fun toString(): String {
return "============="
}复制代码
小结
- lambda是把代码块当成值到处传递
- lambda省略到只用it的写法
- lambda作用范围
- 循环集合forEach,过滤集合filter,映射一个新集合map,求集合最大值maxBy
- 集合的判断,全部符合all,有符合的any,有多少符合的count,找到第一个符合条件的元素find
- 集合分组成map 用方法groupBy方法
- 嵌套集合数据合并flatMap
- 集合转化成序列提高效率,使用 asSequence方法,记得最后把序列再转化成集合
- java中的函数式接口是什么
- 使用with写一个带返回值的lambda表达式