前言
在上一篇中主要讲解了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
.map { animal -> "A baby $animal" }
.map { baby -> "$baby ,with the cutest little tail ever!" }
.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 { 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")
)
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)
val primes = numbers.filter { number ->
!(2 until number).map {
number % it
}.none { it == 0 }
}
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")
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")
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{
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相互调用!
|