Kotlin Flow 常用操作符(take/reduce/zip/flatMap)详解

205 阅读3分钟

一、take:限制收集的元素数量

功能
仅收集流中前 N 个元素,后续元素会被忽略。
适用场景

  • 仅需处理部分数据(如预览前几条数据)。
  • 避免处理过时或无用的后续数据。

示例

flowOf(1, 2, 3, 4, 5)
    .take(3) // 仅收集前 3 个元素(1, 2, 3)
    .collect { println(it) }

// 输出:1 → 2 → 3

二、reduce:累积元素并生成最终结果

功能
将流中的元素依次累积(类似集合的 reduce 操作),最终返回单一结果。
适用场景

  • 求和、求积、字符串拼接等累积操作。
  • 需要将流数据转换为单一聚合值。

示例

val sum = flowOf(1, 2, 3, 4, 5)
    .reduce { acc, value -> acc + value }

println(sum) // 输出:15(1+2+3+4+5)

三、zip:组合两个流的元素

功能
将两个流的元素按顺序一一配对,生成新的组合值。若任一流提前结束,合并结束。
适用场景

  • 合并两个相关流的数据(如用户信息 + 订单信息)。
  • 需要严格按顺序对齐元素。

示例

val numbers = flowOf(1, 2, 3)
val letters = flowOf("A", "B", "C")

numbers.zip(letters) { num, letter -> "$num$letter" }
    .collect { println(it) }

// 输出:1A → 2B → 3C

四、flatMap:扁平化映射流的元素

功能
将每个元素转换为新的流,并将所有生成的流扁平化为单个流。
变体‌:

  • ‌**flatMapConcat**‌:按顺序处理(前一个流结束后才开始下一个)。
  • ‌**flatMapMerge**‌:并发处理多个流(通过 concurrency 参数控制并发数)。
  • ‌**flatMapLatest**‌:仅处理最新的映射流,取消前一个未完成的流。

适用场景

  • 每个元素需要触发独立的异步操作(如请求详情数据)。
  • 需要合并嵌套流的结果。

示例对比

  1. ‌**flatMapConcat(顺序执行)** ‌

    flowOf(1, 2, 3)
        .flatMapConcat { value ->
            flow { 
                emit("A$value")
                delay(100)
                emit("B$value")
            }
        }
        .collect { println(it) }
    
    // 输出:A1 → B1 → A2 → B2 → A3 → B3
    
  2. ‌**flatMapMerge(并发执行)** ‌

    flowOf(1, 2, 3)
        .flatMapMerge(concurrency = 2) { value ->
            flow { 
                emit("A$value")
                delay(100)
                emit("B$value")
            }
        }
        .collect { println(it) }
    
    // 输出顺序可能为:A1 → A2 → B1 → B2 → A3 → B3
    
  3. ‌**flatMapLatest(仅处理最新)** ‌

    flowOf(1, 2, 3)
        .flatMapLatest { value ->
            flow { 
                emit("Start$value")
                delay(200) // 若新元素到达,取消此流
                emit("End$value")
            }
        }
        .collect { println(it) }
    
    // 输出:Start1 → Start2 → Start3 → End3(前两个未完成的任务被取消)
    

五、操作符对比与选型

操作符行为适用场景注意事项
take截取前 N 个元素限制处理范围,避免冗余操作不支持负数或动态计算 N
reduce累积计算结果聚合流数据为单一值流必须非空,否则抛出异常
zip严格按顺序配对元素数据对齐合并任一流结束则合并结束
flatMapConcat顺序展开嵌套流需要严格按顺序处理子流可能因长耗时任务导致背压
flatMapMerge并发展开嵌套流提升子流处理效率需合理设置并发数(默认 16)
flatMapLatest仅保留最新子流响应最新数据(如搜索框输入联想)需处理子流取消逻辑

六、综合实践示例

场景‌:根据用户输入关键词实时搜索,仅保留最后一次请求结果。

// 模拟用户输入流
val queryFlow = flowOf("K", "Ko", "Kot", "Kotlin")

queryFlow
    .debounce(300) // 防抖:300ms 内无新输入才触发
    .flatMapLatest { query ->
        flow { 
            emit("Searching: $query")
            delay(1000) // 模拟网络请求
            emit("Results for: $query")
        }
    }
    .collect { println(it) }

// 输出(假设快速输入):
// Searching: Kotlin → Results for: Kotlin

通过合理组合这些操作符,可以实现复杂的流数据处理逻辑,同时兼顾性能和可维护性