Collections 集合
主要集合: List, Set, Map
需创建可变集合时,添加mutable
val list1: MutableList<Int> = mutableListOf(1, 2, 3)
val list2: List<Int> = ListOf(1, 2, 3)
集合函数
val numbers = listOf(1, -2, 3, -4, 5, -6)
// filter 截取
val positives = numbers.filter { x -> x > 0 }
val negatives = numbers.filter { it < 0 }
// map 映射
val doubled = numbers.map { x -> x * 2 }
val tripled = numbers.map { it * 3 }
// any 存在
val anyNegative = numbers.any { it < 0 }
// all 存在
val allEven = numbers.all { it % 2 == 0 }
// none 存在
val allEven2 = numbers.none { it % 2 == 1 }
// find 查找
val words = listOf("Lets", "find", "something", "in", "collection", "somehow") // 1
val first = words.find { it.startsWith("some") } // 2
val last = words.findLast { it.startsWith("some") } // 3
val nothing = words.find { it.contains("nothing") }
// first 查找
val firstNum = numbers.first()
val lastNum = numbers.last()
val firstEven = numbers.first { it % 2 == 0 } // 4
val lastOdd = numbers.last { it % 2 != 0 }
// 数量
val totalCount = numbers.count() // 2
val evenCount = numbers.count { it % 2 == 0 }
data class Person(val name: String, val city: String, val phone: String) // 1
val people = listOf( // 2
Person("John", "Boston", "+1-888-123456"),
Person("Sarah", "Munich", "+49-777-789123"),
Person("Svyatoslav", "Saint-Petersburg", "+7-999-456789"),
Person("Vasilisa", "Saint-Petersburg", "+7-999-123456"))
val phoneBook = people.associateBy { it.phone } // 3
val cityBook = people.associateBy(Person::phone, Person::city) // 4
val peopleCities = people.groupBy(Person::city, Person::name) // 5
// filter 分割
val evenOdd = numbers.partition { it % 2 == 0 } // ([-2, -4, -6], [1, 3, 5])
val (positives2, negatives2) = numbers.partition { it > 0 }
// flatMap 映射
val tripled2 = numbers.flatMap { listOf(it, it, it) } // []
println(numbers.flatMap { listOf(it, it *it) }) // [1, 1, -2, 4, 3, 9, -4, 16, 5, 25, -6, 36]
// min and max functions 最大最小
val empty = emptyList<Int>()
println("Numbers: $numbers, min = ${numbers.min()} max = ${numbers.max()}") // 1
println("Empty: $empty, min = ${empty.min()}, max = ${empty.max()}") // For empty collections both functions return null.
// sorted 排序
println("Soroted: ${ numbers.sorted()}")
val inverted = numbers.sortedBy { -it }
// 获取map的值
val map = mapOf("key" to 42)
val value1 = map["key"]
val value2 = map["key2"] // 返回null
val value3: Int = map.getValue("key") // 存在正常,不存在异常
val mapWithDefault = map.withDefault { k -> k.length } // 设置默认值
val value4 = mapWithDefault.getValue("key2")
try {
map.getValue("anotherKey") // 4
} catch (e: NoSuchElementException) {
println("Message: $e")
}
// zip 压缩
val A = listOf("a", "b", "c") // 1
val B = listOf(1, 2) // 1
// zip 不能变长
val resultPairs = A zip B // [(a, 1), (b, 2)],
val resultReduce = A.zip(B) { a, b -> "$a$b" } //[a1, b2],
val resultlist = A.zip(B) { a, b -> listOf(a,b) } // [[a, 1], [b, 2]]
// getOrElse 获取
val list = listOf(0, 10, 20)
println(list.getOrElse(1) { 42 })
println(list.getOrElse(10) { 42 })
val map3 = mutableMapOf<String, Int?>()
println(map3.getOrElse("x") { 1 })
map3["x"] = 3
println(map3.getOrElse("x") { 1 })
map3["x"] = null
println(map3.getOrElse("x") { 1 })
isInitialized
lateinit var a:String
//在使用的时候,a未必就一定初始化了,可以两种方式处理
//1,将a声明为String?并=null赋值,那么用的时候,使用a?.去操作,
//2,使用`isInitialized`判断是否初始化了
if(a.isInitialized) //do something
typealias类型别名
class PersonWithLongNameAndErrorSpell{}
//为了方便代码,可以声明类的别名
typealias Person = PersonWithLongNameAndErrorSpell
infix声明中缀函数
中缀函数必须满足的3个条件:
- 必须是成员函数或扩展函数;
- 只能有一个参数;
- 其参数不得接受可变数量的参数且不能有默认值。
即中缀函数为为成员函数或者扩展函数,并且只有一个参数、参数不能是可变参数且不能有默认值, 并且使用infix修饰。
在进行函数调用的时候可以使用中缀方式调用, 优点是具有更强的可读性。
//中缀函数,作用于同类型的数据对象,而且函数声明在类之中
class DemoInt(value:Int){
var mInt = value
//中缀函数声明
infix fun sumInfix(sumTo:DemoInt){
return this.mInt+sumTo.mInt
}
}
//普通的sum函数是
fun sum(a:Int,b:Int) = a+b
//DemoInt使用中缀函数的求和
val a = DemoInt(2)
val b = DemoInt(3)
a sumInfix b//就会得到和一个求和,
kotlin标准库中的to函数 就是一个中缀函数
/**
* Creates a tuple of type [Pair] from this and [that].
*
* This can be useful for creating [Map] literals with less noise, for example:
* @sample samples.collections.Maps.Instantiation.mapFromPairs
*/
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
operator操作符函数重载
//类似中缀函数,在对应类中声明函数,
class DemoInt(value:Int){
var mInt = value
//操作符重载,这里是重载了 + 号
operator fun plus(demo:DemoInt){
println("$mInt , ${demo.mInt}")
}
}
//如此对于DemoInt对象就可以用+操作
val a = DemoInt(2)
val b = DemoInt(3)
a+b//这里就是调用了内部重载的plus函数
函数式编程
1. 函数声明为变量来使用
fun sum(a:Int,b:Int) = a+b
//定义一个不可变量的函数对象,f类型为一个函数表达式,也就是结果Int类型,然后=一个定义好的函数 sum,使用::前缀引用
val f:(Int,Int)->Int = ::sum//也可以使用var,但是val更为合理
//如此就可以在其他地方使用sum函数,用f表示
val result = f(2,3)
//可以结合lambda
val f2:(Int,Int)->Int={a,b->
a+b
}
//进一步
val f3 = {a:Int,b:Int->
a+b
}
2. 类可以继承函数表达式
//类继承函数,就必须实现一个invoke函数,这时候被继承的表达式,类似于接口作用
class Div:(Int,Double)->Double{
override fun invoke(p1: Int, p2: Double): Double {
return (p1 / p2)
}
}
3. 函数表达式作为其他函数的形参
//定义一个函数,表达式(Int,Int)->Int
fun sum(a:Int,b:Int) = a+b
//使用函数形参,也就是理解为,传递来一种算法,
fun callSum(function:(Int,Int)->Int){
val result = function(2,3)//这里只知道使用function计算2,3,并不知道怎么计算,根据传参来决定算法
}
callSum(sum)//传递进一个求和算法,那么就会计算2+3,以此类推
//传参可以是匿名函数,//匿名的(Int,Int)->Int函数,
callSum({a,b->
a+b+20
}
)
这里要注意的,如果函数只有一个参数,可以不用a ->在lambda中,会有一个默认形参it,而且调用处可不用()
val square:(Int)->Int = {it*it}
//匿名函数,求面积,一个参数
fun round(function:(Int)->Int){
println(function(3.14))//这里就是,接收3.14作为参数的fun函数计算
}
//匿名函数的调用方式,传递进去一种算法
round(fun(x:Int) = x*x)
//进一步,lambda 调用,这里传参的为匿名函数(Int)->Int,等效于上面的square
round{it*it}
接收者receiver
//有点类似于扩展函数,可以声明在对象类之外,其他任何需要的地方都行,然后就能用target的对象调用,像它自身的函数一样
class Person{
val name="固定的名字"
}
//声明receiver,这时候的lambda中,没有it参数,而是this,指代当前Person类,在调用时候,就会指引对象
val rec:Person.()->String = {"名字是 $name"}
//然后调用处,有Person的对象就行
val p = Person()
p.rec()
在函数内声明receiver
class Chicken(color:String){
val eggs = mutableListOf<Chicken>()
//function为定义的receiver函数类型,声明?可空,并=null为默认值
fun produce(color:String,function:(Chicken.()->Unit)?=null){
val c = Chicken("red")
eggs.add(c)
if(function!=null){
//调用传来的函数算法
c.function()
}
}
}
//调用处
val cc = Chicken("yellow")
val rec:Chicken.()->Unit = { "这里没有it参数,只有this,所以$color" }
//1、普通调用
cc.produce("green",rec)
//2、匿名函数
cc.produce("red",{"this就是这个对象$this"})
//3、lambda调用,因为是一个参数的表达式,且为最后一个形参
cc.produce("pink"){"这就是lambda的魅力 $this"}
内联函数inline
方法调用流程
调用一个方法是一个压栈和出栈的过程,调用方法时将栈针压入方法栈,然后执行方法体,方法结束时将栈针出栈,这个压栈和出栈的过程会耗费资源,这个过程中传递形参也会耗费资源。
为什么需要inline?
有些简单的方法会被频繁调用,什么叫简单的方法呢,举个例子:
fun <T> check(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
这个check方法的方法体中,不会将它的形参再传递给其他方法。我们调用一下 check 方法:
check(l, {"我是lambda方法体"})//l是一个Lock对象
对于编译器来说,调用 check 方法就要将参数l和 lambda 表达式 {"我是 lambda方法体"}
进行传递,还要将 check 方法进行压栈出栈处理,这个过程就会耗费资源。
如果我们把 check 方法删除,直接执行 check 方法的方法体:
l.lock()
try {
return "我是lambda方法体"
} finally {
l.unlock()
}
这样做的效果和调用 check方法是一样的,而且不需要压栈出栈了,但是代码是写给人看的,这样写明显产生了代码坏味道,老司机会告诉你,这几行代码需要抽成一个方法,避免多处调用产生冗余代码。于是你就老老实实把这几行代码抽成了 check方法,那么如上所述,一旦这个方法被频繁调用,压栈出栈将会带来性能问题。针对这个问题,kotlin 引入了 inline 关键字。我们在 check 方法前加上 inline关键字:
inline fun <T> check(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
然后我们再调用 check 方法,编译器就会在编译期帮我们进行优化:
将我们写的代码
check(l, {"我是lambda方法体"})//l是一个Lock对象
换成
l.lock()
try {
return "我是lambda方法体"
} finally {
l.unlock()
}
也就是说 inline 关键字实际上增加了代码量,但是提升了性能,而且增加的代码量是在编译期执行的,对程序可读性不会造成影响。
其它
如果 check 方法中的参数需要传递给其他非 inline 方法:
inline fun <T> check(lock: Lock, body: () -> T): T {
lock.lock()
try {
otherCheck(body)//会报错
return body()
} finally {
lock.unlock()
}
}
fun <T> otherCheck(body: ()-> T){
}
那么调用 otherCheck 是会报错的,因为 check 方法中的形参 body 现在已经 inline 了,不是一个函数对象了,也就不能作为一个参数传递了,除非在 body 参数前加上 noinline 关键字。
run()函数
//run函数是Any?的扩展函数,可以用于配合?.操作可null的对象
var str:String?=null
//调用方不确定str是否null,但是还有一些函数需要非null的str作为参数
fun callNoNull(str:String){
//do somethins
}
//此时调用可以用?避免NPE,也想非NUll时候运行callNoNull函数,结合run函数
str?.run{callNoNull(this)}
?.run{
callNoNull1(this)
callNoNull2(this)
}
//如此,str为null,则不会调用callNoNull,若非null,则可调用run内的函数,并且,整个表达式返回最后一个run内的函数的返回结果
let()函数
//类似于run()函数,也可以过滤null对象,不同的是,它使用的是普通的函数形参,而run使用的是receiver函数形式
str?.let{callNoNull(it)}
//let函数使用的it,而不是this,同上,可以多次调用,也可以内部多次调用其他函数。并返回最后运算的结果类型
apply()函数
//顾名思义,就是将对象应用于某些操作和算法,这里内部的每个代码块都是拿到的同一个对象,而且不判断null
maybeNull?.apply {
firstFunction(this)
secondFunction(this)
memberPropertyA = memberPropertyB + memberFunctionA()
}?.memberFunctionB()
also()函数
//类似于apply,只是在lambda内,使用it而不是this引用
thkeIf()与thkeUnless()
//takeIf表示摘取仅仅满足条件的数据返回,可能为?,而takeUnless表示提取排除某些条件之外的数据,并返回,也可能为?,所以可以结合配置let函数
array.takeIf{it>12}?.let{it*it}