这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
前言
在上一篇中主要讲解了Kotlin对应的泛型、扩展函数的知识点。在这一篇中,将会讲解Kotlin里面的函数式编程。
话不多说,直接开始!
1、.map{}
使用一个函数之前,先来分析分析源码:
.map源码
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}
复制代码
分析:
Iterable<T>.map(transform: (T) -> R): List<R>
通过这句我们知道,.map
使用者为Iterable<T>
集合类型;: List<R>
这个表示,整个.map
函数返回的是另一个集合列表,返回值以及对应类型由mapTo
方法确定;transform: (T) -> R
因为T在括号里面,说明在map闭包里默认it表示单个元素,返回值类型为返回集合的单个元素类型
接着我们依次看collectionSizeOrDefault、mapTo
这两处源码!
collectionSizeOrDefault源码
internal fun <T> Iterable<T>.collectionSizeOrDefault(default: Int): Int =
if (this is Collection<*>) this.size else default
复制代码
从这句源码可以看出,如果已知,则返回此可迭代对象的大小,否则返回指定的默认值10!
相当于这里定义个新的集合,准备接收后面逻辑来组装新的集合,最后将这个组装好的结合作为List<R>
返回。
接下来
mapTo源码
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C {
for (item in this)
destination.add(transform(item))
return destination
}
复制代码
分析点:
fun<T, R, C : MutableCollection<in R>>
这句代码表示声明对应的泛型,- T 表示原集合里的单个元素类型
- R 表示新集合里的单个元素类型
- C 表示待返回的新集合。里面的
<in R>
这个表示 逆变,类似于 java的? super R
destination: C
这个表示上面通过ArrayList<R>(collectionSizeOrDefault(10))
定义的新集合,待组装数据的集合transform: (T) -> R
这个也是上一个方法传入的方法!for (item in this) destination.add(transform(item)) return destination 复制代码
destination.add(transform(item))
通过这段代码,我们可以知道,闭包里实现的核心逻辑将会重新通过transform
方法重新生成新的元素,然后依次加入新的集合里,最后将其返回!
理论分析完了,原理也知道了,现在来实际实践一番!
fun main{
val animals = listOf("zebra", "giraffe", "elephant", "rat")
val babies = animals
//这里的 animal 等于 集合 animals 遍历后的每一个元素
// map 表示遍历整个对象,并将遍历后的对象返回给上一级,可链式调用
// 这里在 map 里,每个元素 前都拼接有 A baby ,则集合中每个元素前都有 A baby
.map { animal -> "A baby $animal" }
// 这里的baby 等于 等于上面map 拼接后最新集合里遍历的对象
.map { baby -> "$baby ,with the cutest little tail ever!" }
//因为这里执行了最终数据的打印,而println 没有任何返回值,所以这里就返回了 ,集合元素都无返回值的集合
.map { item -> println(item) }
println("---------------------------")
println(animals)
println(babies)
}
复制代码
现在知道对应原理后,再来写代码是不是得心应手?来看看运行效果:
A baby zebra ,with the cutest little tail ever!
A baby giraffe ,with the cutest little tail ever!
A baby elephant ,with the cutest little tail ever!
A baby rat ,with the cutest little tail ever!
---------------------------
[zebra, giraffe, elephant, rat]
[kotlin.Unit, kotlin.Unit, kotlin.Unit, kotlin.Unit]
复制代码
注意:这里面的 animal ->、baby ->、item ->
可直接使用默认it,所以这里是支持自定义命名it的。
2、.filter{}
还是一如既往的先分析源码,只要你跟着我博客看到这,相信你应该具备了一定的分析源码能力,所以后面分析要快点了(就不像之前一步一步解析了)。
进入.filter
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
复制代码
我们看到返回值为List<T>
集合,返回值以及对应的类型由filterTo
确定,所以
进入filterTo
public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C {
for (element in this) if (predicate(element)) destination.add(element)
return destination
}
复制代码
直接抓住重点 for (element in this) if (predicate(element)) destination.add(element)
这句代码表示遍历循环每个元素,如果对应元素满足predicate
这个方法的条件则添加至待返回的集合里。
换句话说,这个方法.filter
对应作用是,过滤集合里面的每个元素,满足条件者则留下来,筛除掉不满足条件的元素!
来看看使用:
fun main{
val result = listOf("Jack", "Jimmy", "Rose", "Tom")
// filter 表示集合过滤 ,对应表达式为true 的数据
.filter { it.contains("J") }
println(result)
}
复制代码
运行效果
[Jack, Jimmy]
复制代码
看看,学习Kotlin是不是很简单,运行效果完完全全按照自己想法来。
3、.flatten()
来看看源码:
public fun <T> Iterable<Iterable<T>>.flatten(): List<T> {
val result = ArrayList<T>()
for (element in this) {
result.addAll(element)
}
return result
}
复制代码
通过这段代码一眼就能看出是多个集合,合并成一个集合的功能。
如果不能一眼看出,那就多看几眼,哈哈哈。
既然这样那使用就简单了!
fun main{
val result =
listOf(listOf(1, 2, 3), listOf(4, 5, 6), listOf("张三", "李四")).flatten()
println(result)
}
复制代码
运行效果
[1, 2, 3, 4, 5, 6, 张三, 李四]
复制代码
完美,所谓知己知彼,方能百战百胜,在这方能得心应手。下一个!
4、.flatMap{}
先看源码
进入.flatMap
public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
return flatMapTo(ArrayList<R>(), transform)
}
复制代码
看到这样的不用多说,直接进入flatMapTo方法
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {
for (element in this) {
val list = transform(element)
destination.addAll(list)
}
return destination
}
复制代码
看到这,这仿佛和上面合并集合差不多,只不过这里合并集合前调用了方法transform
来整理待合并的集合!而这个transform
方法对应逻辑是由开发者自己实现的。所以说试一下?
fun main{
val items = listOf(
listOf("red apple", "green apple", "blue apple"),
listOf("red fish", "blue fish"),
listOf("yellow banana", "teal banana")
)
//这个操作表示 ,过滤 集合 含有 red 的数据,将过滤后的多维集合/数组 合并
val redItems = items.flatMap { it.filter { item-> item.contains("red") } }
println(redItems)
}
复制代码
这里我们实现的逻辑是通过.filter
来过滤对应的元素,拿到符合条件的集合,并且吧对应的集合通过flatMap
合并成一个数组!
运行效果
[red apple, red fish]
复制代码
nice!原来还能这么组合使用!
5、.none{}
先看源码
public inline fun <T> Iterable<T>.none(predicate: (T) -> Boolean): Boolean {
if (this is Collection && isEmpty()) return true
for (element in this) if (predicate(element)) return false
return true
}
复制代码
这里我们看到,这个方法.none
居然是,满足predicate
这个方法时,才会返回false,其他情况都为true。
而.filter
过滤元素时正好需要Boolean来过滤。试试看效果
那就写一个质数过滤器:(已知质数除了本身和1不能被其他整除)
fun main{
val numbers = listOf(7, 4, 8, 4, 3, 22, 18, 11)
// 还是要记住 filter 表示集合过滤, 有啥判断条件就通过它来使用
val primes = numbers.filter { number ->
!(2 until number).map {//所以外部这里再取一个反,表示只要合数 分析3
number % it //这里余数返回0则说明能被除本身外的数整除 分析1
}.none { it == 0 } //如果余数为0 说明是合数,符合表达式,因为里面true为false, 分析2
}
println(primes)
}
复制代码
运行效果
[4, 8, 4, 22, 18]
复制代码
逻辑都在注释里面,按照分析步骤看就OK啦!
6、.zip()
顾名思义zip,感觉像是文件打包压缩,或者说几个文件打包成一个文件。
还是先看源码
public infix fun <T, R> Iterable<T>.zip(other: Iterable<R>): List<Pair<T, R>> {
return zip(other) { t1, t2 -> t1 to t2 }
}
复制代码
进入下一层源码
public inline fun <T, R, V> Iterable<T>.zip(other: Iterable<R>, transform: (a: T, b: R) -> V): List<V> {
val first = iterator()
val second = other.iterator()
val list = ArrayList<V>(minOf(collectionSizeOrDefault(10), other.collectionSizeOrDefault(10)))
while (first.hasNext() && second.hasNext()) {
list.add(transform(first.next(), second.next()))
}
return list
}
复制代码
直接抓住关键点,在while
循环里面list.add(transform(first.next(), second.next()))
这代码表示通过transform
这个方法,将两个集合压缩成一个集合。
而对应的transform
方法就是上一层的{ t1, t2 -> t1 to t2 }
这代码块的实现。
来看看使用效果:
fun main{
val employees = listOf("Jack", "Jason", "Tommy")
val shirtSize = listOf("large", "x-large", "medium")
// zip 顾名思义 压缩的意思,就是将多个集合 合并成一个对象,并可转map 或 list
val employeeShirtSizes=employees.zip(shirtSize)
println(employeeShirtSizes)
}
复制代码
运行效果
[(Jack, large), (Jason, x-large), (Tommy, medium)]
复制代码
这里的结果可以转map以及对应list,来试试?
7、.toMap()
既然知道它是做什么的,那就直接开始用。
fun main{
val employees = listOf("Jack", "Jason", "Tommy")
val shirtSize = listOf("large", "x-large", "medium")
// zip 顾名思义 压缩的意思,就是将多个集合 合并成一个对象,并可转map 或 list
val employeeShirtSizes=employees.zip(shirtSize).toMap()
val list = employeeShirtSizes.map { "key= ${it.key}, value=${it.value} \t" }
println(list)
}
复制代码
运行效果
[key= Jack, value=large , key= Jason, value=x-large , key= Tommy, value=medium ]
复制代码
没啥可说的,读者也可尝试一下转list玩玩,在这就不试了,直接下一个!
8、.fold(){}
继续看源码
public inline fun <T, R> Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
var accumulator = initial
for (element in this) accumulator = operation(accumulator, element)
return accumulator
}
复制代码
源码分析:
initial: R
这个表示fold(a)
里面对应a的值,也就是说,accumulator
为fold(a)
方法a的值;- 接着看
for (element in this) accumulator = operation(accumulator, element)
这段代码: - 这句代码:
accumulator = operation(accumulator, element)
表示每次调用operation
方法后,将方法结果赋值给accumulator
; - 当执行下一个循环时,会将上一个结果值作为
operation
方法的第一个形参传入,随后将方法结果赋值给accumulator
,就这样依次循环到结束!
分析完了,看看使用!
fun main{
// fold 表示 集合遍历时 会将对应变量作为初始值带入集合遍历循环里,遍历对象第一个表示带入初始值,第二个表示集合item
val foledValue = listOf(1, 2, 3, 4).fold(3) { accmulator, number ->
println("Accmulator value:$accmulator,number value:$number")
accmulator + (number * 3)
}
println("Final value:$foledValue")
}
复制代码
运行效果
Accmulator value:3,number value:1
Accmulator value:6,number value:2
Accmulator value:12,number value:3
Accmulator value:21,number value:4
Final value:33
复制代码
第一次 accmulator
对应的值就为fold(3)
传入的3,开始循环后对应accmulator
的值,都为上一次循环后的结果。
9、.take()
还是先看源码
public fun <T> Iterable<T>.take(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
if (this is Collection<T>) {
if (n >= size) return toList()
if (n == 1) return listOf(first())
}
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
list.add(item)
if (++count == n)
break
}
return list.optimizeReadOnlyList()
}
复制代码
仔细看这段代码:
for (item in this) {
list.add(item)
if (++count == n)
break
}
复制代码
乍一看好像是最多返回长度为n的集合,试试看!
fun Int.isPrime(): Boolean {
(2 until this).map {
if (this % it == 0) {
return false
}
}
return true
}
fun main{
val toList = (1..5000).toList().filter { it.isPrime() }.take(1000)
println("在5000以内并且最多找1000个素数之间,找到 ${toList.size} 个素数,最后一个素数为:${toList[toList.size - 1]}")
val oneTousandPrimes = generateSequence(2) { value ->
value + 1
}.filter { it.isPrime() }.take(1000)
val listPrimes = oneTousandPrimes.toList()
println("在自然数自动增长情况下最多找1000个素数,找到 ${listPrimes.size} 个素数,最后一个素数为:${listPrimes[listPrimes.size - 1]}")
}
复制代码
来看看效果:
在5000以内并且最多找1000个素数之间,找到670个素数,最后一个素数为:4999
在自然数自动增长情况下最多找1000个素数,找到1000个素数,最后一个素数为:7919
复制代码
结束语
好了,本篇到这差不多就结束了。在下一篇中,将会详解,Kotlin与Java相互调用!