Android—Kotiln基础教程(八)

这是我参与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)
}
复制代码

分析:

  1. Iterable<T>.map(transform: (T) -> R): List<R>通过这句我们知道,.map使用者为Iterable<T>集合类型;
  2. : List<R>这个表示,整个.map函数返回的是另一个集合列表,返回值以及对应类型由mapTo方法确定;
  3. 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
}
复制代码

分析点:

  1. fun<T, R, C : MutableCollection<in R>> 这句代码表示声明对应的泛型,
    1. T 表示原集合里的单个元素类型
    2. R 表示新集合里的单个元素类型
    3. C 表示待返回的新集合。里面的 <in R> 这个表示 逆变,类似于 java的 ? super R
  2. destination: C这个表示上面通过ArrayList<R>(collectionSizeOrDefault(10))定义的新集合,待组装数据的集合
  3. transform: (T) -> R 这个也是上一个方法传入的方法!
    for (item in this)
        destination.add(transform(item))
    return destination
    复制代码
  4. 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相互调用!

猜你喜欢

转载自juejin.im/post/7030738726443548685