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

相关推荐
想唱rap2 小时前
Linux开发工具(4)
linux·运维·服务器·开发语言·算法
Geoking.2 小时前
【Java】Java 中 @Resource 与 @Autowired 的区别详解
java·开发语言
生而为虫2 小时前
02.第一个Python程序
开发语言·python
星释2 小时前
Rust 练习册 :Rail Fence Cipher与栅栏密码
开发语言·算法·rust
weixin_441455262 小时前
说说Java有哪些集合类
java·开发语言
李趣趣3 小时前
C#中关于ContextMenuStrip批量添加Item的问题
开发语言·c#
张人玉3 小时前
C# 串口通讯中 SerialPort 类的关键参数和使用方法
开发语言·c#·串口通讯
白山云北诗3 小时前
网站被攻击了怎么办?如何进行DDoS防御?
开发语言·网络安全·php·ddos·防ddos·防cc
stevenzqzq3 小时前
Android Hilt 入门教程_注解汇总
android