用通俗易懂 + 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 最基础、最常用的"组合积木"之一。

相关推荐
沐知全栈开发1 小时前
jEasyUI 设置排序
开发语言
code bean1 小时前
【C++ 】C++ 与 C#:using 关键字、命名空间及作用域解析符对比
开发语言·c++·c#
CAE虚拟与现实1 小时前
C# 调用 DLL为什么不像 C/C++调用 DLL 时需要lib库
开发语言·c++·c#·动态链接库·dll库·lib库
Larry_Yanan1 小时前
Qt线程使用(一)直接继承QThread类
开发语言·c++·qt·ui
vortex51 小时前
Bash One-Liners 学习精要指南
开发语言·chrome·bash
j***89461 小时前
MySQL数据的增删改查(一)
android·javascript·mysql
Yu_Lijing2 小时前
【个人项目】C++基于websocket的多用户网页五子棋(上)
开发语言·c++·websocket
脏脏a2 小时前
【初阶数据结构】栈与队列:定义、核心操作与代码解析
c语言·开发语言
济宁雪人2 小时前
Java安全基础——序列化/反序列化
java·开发语言
q***01772 小时前
Java进阶--IO流
java·开发语言