美文网首页
【Koltin Flow(二)】Flow操作符之末端操作符

【Koltin Flow(二)】Flow操作符之末端操作符

作者: SnowJun | 来源:发表于2022-07-29 11:27 被阅读0次

    目录

    【Koltin Flow(一)】五种创建flow的方式
    【Koltin Flow(二)】Flow操作符之末端操作符
    【Koltin Flow(三)】Flow操作符之中间操作符(一)
    【Koltin Flow(三)】Flow操作符之中间操作符(二)
    【Koltin Flow(三)】Flow操作符之中间操作符(三)

    简介

    1. 本篇主要介绍flow的操作符部分,
    2. Flow操作符主要分为末端操作符和中间操作符,本篇主要介绍末端操作符。
    3. 本篇会介绍各类操作符的作用和使用方式,当然操作符比较多,也不会全面覆盖,会选择终点进行讲解。

    末端操作符

    末端操作符在flow处理的最后,主要用来接收数据,当然flow为冷流(这里不做展开,后面再说),只有在末端操作符的时候才会执行操作,通常的接收数据都离不开末端操作符。
    
    1. collect 在上一篇的五种方式的时候就用到了,特点就是依次接收到前面发送过来的数据。
    2. collectIndexed 接收的时候会带有index下标,可以用来处理下标相关的需求,
    代码如下:
                flowOf(1,2,3,4,5).collectIndexed {index,value->
                    Log.d(TAG.TAG,"position[$index] it is $value")
                }
    
    日志如下:
    2022-07-28 17:34:49.619 3842-3868/edu.test.demo D/Test-TAG: position[0] it is 1
    2022-07-28 17:34:49.619 3842-3868/edu.test.demo D/Test-TAG: position[1] it is 2
    2022-07-28 17:34:49.619 3842-3868/edu.test.demo D/Test-TAG: position[2] it is 3
    2022-07-28 17:34:49.619 3842-3868/edu.test.demo D/Test-TAG: position[3] it is 4
    2022-07-28 17:34:49.619 3842-3868/edu.test.demo D/Test-TAG: position[4] it is 5
    
    1. collectLatest 当数据来不及处理的时候只会保存最新的数据,这会牵扯到背压(这里不做展开,后面讲解)。
      现象1,正常处理的时候和collect没什么区别,都是一直接收数据:
    代码如下:
                flowOf(1,2,3,4,5).collectLatest {value->
                    Log.d(TAG.TAG,"it is $value")
                }
    
    日志如下:
    2022-07-28 17:37:23.973 3903-3929/edu.test.demo D/Test-TAG: it is 1
    2022-07-28 17:37:23.980 3903-3929/edu.test.demo D/Test-TAG: it is 2
    2022-07-28 17:37:23.981 3903-3929/edu.test.demo D/Test-TAG: it is 3
    2022-07-28 17:37:23.981 3903-3929/edu.test.demo D/Test-TAG: it is 4
    2022-07-28 17:37:23.981 3903-3929/edu.test.demo D/Test-TAG: it is 5
    

    现象2,来不及处理的时候只会保持最新的数据:

    代码如下:
                flowOf(1,2,3,4,5).collectLatest {value->
                    delay(10)
                    Log.d(TAG.TAG,"it is $value")
                }
    
    日志如下:
    2022-07-28 17:37:39.922 3959-3985/edu.test.demo D/Test-TAG: it is 5
    
    1. single、singleOrNull、first、firstOrNull,single及singleOrNull接收单一值,区别是在flow为空或者多余一个值得时候会抛出异常,但是singleOrNull只是返回null,并不会抛出异常。first、firstOrNull都是接收第一个值,区别是当flow为空时候,first会抛出异常,但是firstOrNull只是会返回null,并不会抛出异常。
    代码如下:
     val flow1 = flow {
                    emit(111)
                }
                val flow2 = flow {
                    emit(111)
                    emit(1110)
                }
                val flow3 = flow<String> {
                }
    
                launch {
                    Log.d(TAG.TAG, "single 1 data is ${flow1.single()}")
                    Log.d(TAG.TAG, "singleOrNull 1 data is ${flow1.singleOrNull()}")
                    kotlin.runCatching {
                        Log.d(TAG.TAG, "single 2 data is ${flow2.single()}")
                    }.onFailure {
                        Log.e(TAG.TAG,"single 2 throwable is $it")
                    }
                    Log.d(TAG.TAG, "singleOrNull 2 data is ${flow2.singleOrNull()}")
                    Log.d(TAG.TAG, "first 2 data is ${flow2.first()}")
                    Log.d(TAG.TAG, "firstOrNull 2 data is ${flow2.firstOrNull()}")
                    kotlin.runCatching {
                        Log.d(TAG.TAG, "first 3 data is ${flow3.first()}")
                    }.onFailure {
                        Log.e(TAG.TAG,"first 3 throwable is $it")
                    }
                    Log.d(TAG.TAG, "firstOrNull 3 data is ${flow3.firstOrNull()}")
                }
    
    日志如下:
    2022-07-29 10:04:02.767 5737-5763/edu.test.demo D/Test-TAG: single 1 data is 111
    2022-07-29 10:04:02.768 5737-5763/edu.test.demo D/Test-TAG: singleOrNull 1 data is 111
    2022-07-29 10:04:02.769 5737-5763/edu.test.demo E/Test-TAG: single 2 throwable is java.lang.IllegalArgumentException: Flow has more than one element
    2022-07-29 10:04:02.769 5737-5763/edu.test.demo D/Test-TAG: singleOrNull 2 data is null
    2022-07-29 10:04:02.769 5737-5763/edu.test.demo D/Test-TAG: first 2 data is 111
    2022-07-29 10:04:02.770 5737-5763/edu.test.demo D/Test-TAG: firstOrNull 2 data is 111
    2022-07-29 10:04:02.770 5737-5763/edu.test.demo E/Test-TAG: first 3 throwable is java.util.NoSuchElementException: Expected at least one element
    2022-07-29 10:04:02.770 5737-5763/edu.test.demo D/Test-TAG: firstOrNull 3 data is null
    
    分析:
    • 当flow为一个值的时候 single 和singleOrNull 都是正常的,但是当flow为两个值得时候single抛出了异常single 2 throwable is java.lang.IllegalArgumentException: Flow has more than one element,而singleOrNull 返回为null,没有排除异常。
    • 当flow不为空的时候则,first和firstOrNull都是正常的,但是当flow为空,first抛出了异常first 3 throwable is java.util.NoSuchElementException: Expected at least one element,但是firstOrNull 返回为null,并没有抛出异常。
    1. reduce和fold,reduce是进行累计操作,初始值为第一个值和下一个值,fold也为累计操作,初始值为设置的初始值和第一个值。
    代码如下:
           val reduceData = (1..5).asFlow().reduce { accumulator, value -> accumulator + value }
                Log.d(TAG.TAG, "reduceData is $reduceData")
                val foldData = (1..5).asFlow().fold(1000) { acc, value -> acc + value }
                Log.d(TAG.TAG, "foldData is $foldData")
           val flow = flow<Int> {  }
                kotlin.runCatching {
                    Log.d(TAG.TAG, "reduceEmptyData is ${flow.reduce { accumulator, value -> accumulator + value }}")
                }.onFailure {
                    Log.e(TAG.TAG,"reduceEmptyData throwable is $it ")
                }
                Log.d(TAG.TAG, "foldEmptyData is ${flow.fold(10) { acc, value -> acc + value }}")
    
    日志如下:
    2022-07-29 10:45:24.808 6321-6346/edu.test.demo D/Test-TAG: reduceData is 15
    2022-07-29 10:45:24.808 6321-6346/edu.test.demo D/Test-TAG: foldData is 1015
    2022-07-29 10:45:24.810 6321-6346/edu.test.demo E/Test-TAG: reduceEmptyData throwable is java.util.NoSuchElementException: Empty flow can't be reduced 
    2022-07-29 10:45:24.810 6321-6346/edu.test.demo D/Test-TAG: foldEmptyData is 10
    
    分析:
    • 可以看出reduce是进行了1-5的累计,结果为15,而fold则是在1000的基础上再加上1-5的累加,结果为1015。
    • 当然也有人有疑问,如果我后面不写 accumulator, value -> accumulator + value ,和 acc, value -> acc + value ,把加换成减行不行,当然是可以的,reduce和fold只是进行顺序积累,只是换成减就不是累加操作,是其他操作了。
    • flow为空,reduce 会抛出异常 reduceEmptyData throwable is java.util.NoSuchElementException: Empty flow can't be reduced,fold则返回初始值,不会抛出异常。
    1. toList和toSet,toList将flow转换成list结果,参数为mutableList,可传可不传,但是传参注意不要多次调用,会把值多次添加进去。toSet类似,但是toSet传入初始值多次调用不会产生重复值,因为set本身就是不重复的。
    代码如下 :
                //list
                val flow = flow<Int> {
                    repeat(10){
                        delay(10)
                        emit(it)
                    }
                }
                Log.d(TAG.TAG,"flow.toList() is ${flow.toList()}")
                val lisDataInit = arrayListOf<Int>()
                val listData = flow.toList(lisDataInit)
                flow.toList(lisDataInit)
                Log.d(TAG.TAG,"listData is $listData")
                Log.d(TAG.TAG,"lisDataInit is $lisDataInit")
            //set
             val flow = flow<Int> {
                    repeat(10){
                        delay(10)
                        emit(it)
                    }
                }
                val setDataInit = LinkedHashSet<Int>()
                Log.d(TAG.TAG,"flow.toSet() is ${flow.toSet()}")
                val setData = flow.toSet(setDataInit)
                flow.toSet(setDataInit)
                Log.d(TAG.TAG,"setDataInit is $setData")
                Log.d(TAG.TAG,"setDataInit is $setDataInit")
    
    日志如下:
    2022-07-29 10:55:48.927 6810-6835/edu.test.demo D/Test-TAG: flow.toList() is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    2022-07-29 10:55:49.157 6810-6835/edu.test.demo D/Test-TAG: listData is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    2022-07-29 10:55:49.157 6810-6835/edu.test.demo D/Test-TAG: lisDataInit is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    2022-07-29 11:00:30.678 6896-6922/edu.test.demo D/Test-TAG: flow.toSet() is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    2022-07-29 11:00:30.908 6896-6922/edu.test.demo D/Test-TAG: setData is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    2022-07-29 11:00:30.908 6896-6922/edu.test.demo D/Test-TAG: setDataInit is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    分析:
    • 可以看出 toList直接将flow的发送转换成list结果,toSet直接将结果转换成Set结果。。
    • 可以看出传入list多次调用,会把值多次添加,listData和lisDataInit都讲数据添加了两遍,这两个其实是一个list,set类似,但是set本身是不重复的,所以不会产生多次添加进去的结果。从源码可以看出,源码如下:
    /**
     * Collects given flow into a [destination]
     */
    public suspend fun <T> Flow<T>.toList(destination: MutableList<T> = ArrayList()): List<T> = toCollection(destination)
    
    /**
     * Collects given flow into a [destination]
     */
    public suspend fun <T> Flow<T>.toSet(destination: MutableSet<T> = LinkedHashSet()): Set<T> = toCollection(destination)
    
    /**
     * Collects given flow into a [destination]
     */
    public suspend fun <T, C : MutableCollection<in T>> Flow<T>.toCollection(destination: C): C {
        collect { value ->
            destination.add(value)
        }
        return destination
    }
    

    总结

    1. 本篇主要介绍了末端操作符的,包含collect 、collectIndex、collectLatest 、single、singleOrNull、first、firstOrNull、 reduce、fold、toList、toSet等。
    2. 末端操作符只要用来数据的收集,如collect按顺序收集,single单一收集,toList转换成list结果等。
    3. 其实操作符本身也不难,只要我们多用用,再可以点开源码多看看,其实理解起来也是比较容易的。
    4. 本篇也是在自己学习及使用的过程当中总结的,难免存在错误及思维局限,还希望大家多多指出与交流。

    相关文章

      网友评论

          本文标题:【Koltin Flow(二)】Flow操作符之末端操作符

          本文链接:https://www.haomeiwen.com/subject/hvtqwrtx.html