用通俗易懂 + Android 开发实战的方式讲解 Kotlin Flow 中的 filter 操作符

一、一句话理解 filter

filter 的作用是:只让"符合条件"的数据通过,不符合的直接丢掉。

你可以把它想象成一个"筛子":

  • 数据流进来
  • filter 按你定的规则检查每个值
  • 只有满足条件的才能流出去,其他的被过滤掉

二、基本语法

复制代码
flow.filter { value ->
    // 返回 true:保留这个值
    // 返回 false:丢弃这个值
}

三、简单例子 🌰

假设你有一个发出数字的 Flow:

复制代码
val numbers = flowOf(1, 2, 3, 4, 5, 6)

你只想处理偶数

复制代码
val evenNumbers = numbers.filter { it % 2 == 0 }
// 结果 Flow 会发出:2, 4, 6

四、Android 实战场景 📱

场景1:只显示"已激活"的用户

假设你从数据库获取用户列表:

复制代码
data class User(val id: Int, val name: String, val isActive: Boolean)

// Repository 返回 Flow<List<User>>
val allUsers: Flow<List<User>> = userRepository.getAllUsers()

但 UI 只想显示 isActive == true 的用户。

✅ 用 filter(注意:这里是对列表中的每个用户过滤):

复制代码
val activeUsers: Flow<List<User>> = allUsers.map { users ->
    users.filter { user -> user.isActive } // 这是 List 的 filter
}

🔍 注意:这里用的是 List 的 filter ,因为 Flow 发出的是整个列表。

如果 Flow 是逐个发出 User(比如 Flow<User>),那就直接用 Flow 的 filter

复制代码
val activeUserStream: Flow<User> = userEventStream.filter { it.isActive }

场景2:搜索框防抖后过滤空输入

用户在搜索框输入,你用 callbackFlowStateFlow 监听输入:

复制代码
val searchQueries: StateFlow<String> = mutableSearchQuery.asStateFlow()

你不希望在用户输入空字符串或长度太短时发起网络请求:

复制代码
lifecycleScope.launch {
    searchQueries
        .map { it.trim() }          // 先 trim
        .filter { it.length >= 2 }  // 只有 >=2 个字符才继续
        .debounce(300)              // 防抖
        .collect { query ->
            searchRepository.search(query) // 安全发起请求
        }
}

✅ 这样就避免了无效请求(比如搜一个字母或空格)。


五、filter 的关键特点

特点 说明
同步判断 lambda 里不能直接调用 suspend 函数(和 map 一样)
🔽 可能"卡住" 如果所有数据都不符合条件,下游就收不到任何值(这是正常行为!)
🔄 不改变数据类型 输入是 Flow<T>,输出还是 Flow<T>,只是元素变少了

六、常见误区

❌ 误区1:在 filter 里做网络请求或数据库查询
复制代码
someFlow.filter { value ->
    someSuspendFunction(value) // ❌ 编译错误!不能挂起
}

✅ 正确做法:如果需要异步判断,考虑用 transform 或提前在 Repository 层处理。

❌ 误区2:以为 filter 会"跳过" collect
复制代码
flow.filter { it > 5 }.collect { println(it) }
// 如果所有值 ≤5,collect 里的代码根本不会执行 ------ 这是正确的!

这其实是 filter 的正常行为:没有符合条件的数据,就不发射


七、和其他操作符搭配使用(超常用!)

Flow 的强大在于链式调用,比如:

复制代码
flowOf("apple", "banana", "cherry", "date")
    .filter { it.length > 5 }     // 只保留长度 >5 的
    .map { it.uppercase() }       // 转大写
    .collect { println(it) }      // 输出:BANANA, CHERRY

在 Android 中,你经常会看到这样的链:

复制代码
searchQuery
    .map { it.trim() }
    .filter { it.isNotEmpty() }
    .debounce(400)
    .distinctUntilChanged()
    .map { performSearch(it) }
    .catch { showError(it) }
    .collect { updateUI(it) }

八、总结一句话

filter 是 Flow 中用来"筛选数据"的操作符,只让满足条件的元素继续流动,非常适合在 Android 开发中做输入验证、状态过滤、避免无效操作等场景。

它和 map 一样,是 Flow 最基础、最常用的"组合积木"之一。

相关推荐
赏金术士8 分钟前
Retrofit + Kotlin 协程(Android 实战教程)
android·kotlin·retrofit
坚果派·白晓明6 小时前
【鸿蒙PC三方库移植适配框架解读系列】第八篇:扩展lycium框架使其满足rust三方库适配
c语言·开发语言·华为·rust·harmonyos·鸿蒙
花间相见6 小时前
【PaddleOCR教程01】PP-OCRv5 全面指南:从模型架构到实战部署
开发语言·r语言
小短腿的代码世界7 小时前
Qt 股票订单撮合引擎:高频交易系统的核心心脏
开发语言·数据库·qt·系统架构·交互
大炮筒7 小时前
COCOS2DX4.0CPPWIN移植安卓踩坑总结
android
谙弆悕博士8 小时前
快速学C语言——第16章:预处理
c语言·开发语言·chrome·笔记·创业创新·预处理·业界资讯
yuan199979 小时前
基于 C# 实现的 Omron HostLink (FINS) 协议 PLC 通讯
开发语言·c#
qq_422828629 小时前
android图形学之SurfaceControl和Surface的关系 五
android·开发语言·python
如竟没有火炬10 小时前
用队列实现栈
开发语言·数据结构·python·算法·leetcode·深度优先
折哥的程序人生 · 物流技术专研10 小时前
《Java 100 天进阶之路》第17篇:Java常用包装类与自动装箱拆箱深入
java·开发语言·后端·面试