Kotlin 过滤函数 filter 详解
filter 是 Kotlin 集合处理中最常用的函数之一,用于从集合中筛选出满足特定条件的元素。
基本用法
1. 基础过滤
kotlin
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// 筛选偶数
val evens = numbers.filter { it % 2 == 0 }
println(evens) // [2, 4, 6, 8, 10]
// 筛选大于5的数
val greaterThanFive = numbers.filter { it > 5 }
println(greaterThanFive) // [6, 7, 8, 9, 10]
// 筛选包含特定字符的字符串
val fruits = listOf("apple", "banana", "cherry", "date")
val aFruits = fruits.filter { it.contains('a') }
println(aFruits) // [apple, banana, date]
2. 不同类型集合的过滤
kotlin
// List
val list = listOf(1, 2, 3, 4, 5)
val filteredList = list.filter { it > 2 }
// Set
val set = setOf("a", "b", "c", "d")
val filteredSet = set.filter { it in listOf("a", "c") }
// Map - 过滤条目
val map = mapOf("a" to 1, "b" to 2, "c" to 3, "d" to 4)
val filteredMap = map.filter { (key, value) ->
key > "b" && value % 2 == 0
}
println(filteredMap) // {d=4}
// Array
val array = arrayOf(1, 2, 3, 4, 5)
val filteredArray = array.filter { it > 3 }
filter 的变体函数
1. filterNot() - 反向过滤
kotlin
val numbers = listOf(1, 2, 3, 4, 5, 6)
// 过滤掉偶数(保留奇数)
val odds = numbers.filterNot { it % 2 == 0 }
println(odds) // [1, 3, 5]
// 等价于
val odds2 = numbers.filter { it % 2 != 0 }
2. filterIndexed() - 带索引的过滤
kotlin
val numbers = listOf(10, 20, 30, 40, 50)
// 根据索引和值进行过滤
val result = numbers.filterIndexed { index, value ->
index % 2 == 0 && value > 20
}
println(result) // [30, 50]
// 复杂的索引条件
val items = listOf("a", "b", "c", "d", "e")
val filtered = items.filterIndexed { index, item ->
index > 1 && item in listOf("c", "e")
}
println(filtered) // [c, e]
3. filterIsInstance<T>() - 类型过滤
kotlin
val mixedList: List<Any> = listOf(1, "hello", 2.5, "world", 3, 4.0)
// 过滤出所有整数
val integers = mixedList.filterIsInstance<Int>()
println(integers) // [1, 3]
// 过滤出所有字符串
val strings = mixedList.filterIsInstance<String>()
println(strings) // [hello, world]
// 过滤出所有浮点数
val doubles = mixedList.filterIsInstance<Double>()
println(doubles) // [2.5, 4.0]
4. filterNotNull() - 过滤空值
kotlin
val listWithNulls = listOf(1, null, 2, null, 3, null, 4)
// 过滤掉所有null值
val withoutNulls = listWithNulls.filterNotNull()
println(withoutNulls) // [1, 2, 3, 4]
// 在链式调用中使用
val nullableStrings = listOf("a", null, "b", "c", null)
val upperCaseLetters = nullableStrings
.filterNotNull()
.filter { it.length == 1 }
.map { it.uppercase() }
println(upperCaseLetters) // [A, B, C]
5. filterTo() - 过滤到指定集合
kotlin
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// 将过滤结果添加到现有的可变集合中
val result = mutableListOf<Int>()
numbers.filterTo(result) { it % 2 == 0 }
println(result) // [2, 4, 6, 8, 10]
// 也可以用于不同集合类型
val setResult = mutableSetOf<Int>()
numbers.filterTo(setResult) { it > 5 }
println(setResult) // [6, 7, 8, 9, 10]
6. Map 的专用过滤函数
kotlin
val map = mapOf(
"apple" to 10,
"banana" to 20,
"cherry" to 15,
"date" to 5
)
// filterKeys - 只根据键过滤
val aKeys = map.filterKeys { it.startsWith("a") }
println(aKeys) // {apple=10}
// filterValues - 只根据值过滤
val highValues = map.filterValues { it > 10 }
println(highValues) // {banana=20, cherry=15}
实际应用示例
1. 数据清洗和转换
kotlin
data class Product(
val id: Int,
val name: String,
val price: Double,
val category: String,
val inStock: Boolean
)
val products = listOf(
Product(1, "Laptop", 999.99, "Electronics", true),
Product(2, "Mouse", 29.99, "Electronics", false),
Product(3, "Desk", 199.99, "Furniture", true),
Product(4, "Chair", 149.99, "Furniture", true),
Product(5, "Keyboard", 79.99, "Electronics", true),
Product(6, "Monitor", 299.99, "Electronics", false)
)
// 筛选有库存的电子产品
val availableElectronics = products.filter {
it.category == "Electronics" && it.inStock
}
println("有库存的电子产品: ${availableElectronics.size}")
// 筛选价格在100到300之间的产品
val midRangeProducts = products.filter {
it.price in 100.0..300.0
}
println("中价位产品: ${midRangeProducts.size}")
// 复杂的组合条件
val specialProducts = products.filter { product ->
(product.category == "Electronics" && product.price < 100) ||
(product.category == "Furniture" && product.inStock)
}
println("特殊产品: ${specialProducts.size}")
2. 用户输入验证
kotlin
data class UserInput(
val username: String,
val email: String,
val password: String,
val age: Int?
)
fun validateInputs(inputs: List<UserInput>): List<UserInput> {
return inputs.filter { input ->
// 用户名:3-20个字符,只包含字母数字
input.username.matches(Regex("^[a-zA-Z0-9]{3,20}$")) &&
// 邮箱:有效格式
input.email.matches(Regex("^[A-Za-z0-9+_.-]+@(.+)$")) &&
// 密码:至少8个字符,包含大小写和数字
input.password.matches(Regex("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$")) &&
// 年龄:18-120之间
(input.age == null || input.age in 18..120)
}
}
val userInputs = listOf(
UserInput("john123", "john@email.com", "Pass123", 25),
UserInput("ab", "bad-email", "weak", 15),
UserInput("jane_doe", "jane@email.com", "StrongPass123", 30)
)
val validInputs = validateInputs(userInputs)
println("有效输入数量: ${validInputs.size}")
3. API 响应处理
kotlin
data class ApiResponse<T>(
val data: T?,
val error: String?,
val statusCode: Int
)
data class User(val id: String, val name: String, val active: Boolean)
fun processResponses(responses: List<ApiResponse<List<User>>>): List<User> {
return responses
// 过滤成功的响应
.filter { it.statusCode == 200 && it.error == null }
// 获取数据
.flatMap { it.data ?: emptyList() }
// 过滤活跃用户
.filter { it.active }
// 去重
.distinctBy { it.id }
}
val apiResponses = listOf(
ApiResponse(
data = listOf(User("1", "Alice", true), User("2", "Bob", false)),
error = null,
statusCode = 200
),
ApiResponse(
data = null,
error = "Server error",
statusCode = 500
),
ApiResponse(
data = listOf(User("1", "Alice", true), User("3", "Charlie", true)),
error = null,
statusCode = 200
)
)
val activeUsers = processResponses(apiResponses)
println("活跃用户: $activeUsers")
4. 文件处理
kotlin
data class FileInfo(
val name: String,
val size: Long,
val extension: String,
val lastModified: Long
)
fun filterFiles(files: List<FileInfo>): List<FileInfo> {
val now = System.currentTimeMillis()
val oneWeekAgo = now - (7 * 24 * 60 * 60 * 1000)
return files.filter { file ->
// 最近一周内修改过的文件
file.lastModified > oneWeekAgo &&
// 特定类型的文件
file.extension in listOf("txt", "pdf", "docx") &&
// 文件大小在合理范围内
file.size in 1024..(10 * 1024 * 1024) && // 1KB - 10MB
// 文件名不含特殊字符
file.name.matches(Regex("^[a-zA-Z0-9._ -]+\\.${file.extension}$"))
}
}
性能优化技巧
1. 避免重复过滤
kotlin
// 不好:重复过滤和转换
val numbers = (1..1000).toList()
val badExample = numbers
.filter { it % 2 == 0 } // 第一次遍历
.map { it * 2 } // 第二次遍历
.filter { it > 100 } // 第三次遍历
// 好:合并过滤条件
val goodExample = numbers
.filter {
val isEven = it % 2 == 0
val doubled = it * 2
isEven && doubled > 100
}
.map { it * 2 }
2. 使用序列处理大数据集
kotlin
val largeList = (1..1_000_000).toList()
// 普通方式:创建中间集合
val regular = largeList
.filter { it % 2 == 0 } // 创建中间集合
.map { it * 2 } // 创建中间集合
.take(10) // 取前10个
// 使用序列:惰性求值,更高效
val withSequence = largeList.asSequence()
.filter { it % 2 == 0 } // 惰性过滤
.map { it * 2 } // 惰性映射
.take(10) // 只取10个
.toList() // 最后转换为列表
3. 缓存频繁使用的谓词
kotlin
val numbers = (1..1000).toList()
// 重复计算谓词
val predicates = listOf(
{ n: Int -> n % 2 == 0 },
{ n: Int -> n % 3 == 0 },
{ n: Int -> n > 100 }
)
// 一次性应用多个过滤器
val result = numbers.filter { number ->
predicates.all { predicate -> predicate(number) }
}
与 partition 的对比
filter 返回满足条件的元素,partition 将集合分成两个列表。
kotlin
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// 使用 filter 和 filterNot
val evens = numbers.filter { it % 2 == 0 }
val odds = numbers.filterNot { it % 2 == 0 }
// 使用 partition(更高效,只遍历一次)
val (evensPart, oddsPart) = numbers.partition { it % 2 == 0 }
println("偶数: $evensPart") // [2, 4, 6, 8, 10]
println("奇数: $oddsPart") // [1, 3, 5, 7, 9]
自定义过滤条件
1. 创建可重用的谓词函数
kotlin
// 定义谓词函数
val isEven: (Int) -> Boolean = { it % 2 == 0 }
val isPositive: (Int) -> Boolean = { it > 0 }
val inRange: (Int) -> (Int) -> Boolean = { min -> { max ->
{ value -> value in min..max }
}}
// 使用谓词函数
val numbers = listOf(-5, -2, 0, 3, 7, 10, 15)
val positiveEvens = numbers.filter { isEven(it) && isPositive(it) }
val inRange10to20 = numbers.filter { inRange(10)(20)(it) }
// 组合谓词
fun <T> composePredicates(vararg predicates: (T) -> Boolean): (T) -> Boolean {
return { item -> predicates.all { it(item) } }
}
val complexFilter = composePredicates(isPositive, isEven)
val result = numbers.filter(complexFilter)
2. 面向对象的过滤策略
kotlin
interface FilterStrategy<T> {
fun shouldInclude(item: T): Boolean
}
class PriceFilter(private val minPrice: Double, private val maxPrice: Double) :
FilterStrategy<Product> {
override fun shouldInclude(item: Product): Boolean {
return item.price in minPrice..maxPrice
}
}
class CategoryFilter(private val category: String) : FilterStrategy<Product> {
override fun shouldInclude(item: Product): Boolean {
return item.category == category
}
}
// 使用策略模式过滤
fun <T> List<T>.filterWith(strategies: List<FilterStrategy<T>>): List<T> {
return this.filter { item ->
strategies.all { strategy -> strategy.shouldInclude(item) }
}
}
val products = listOf(
Product(1, "Laptop", 999.99, "Electronics", true),
Product(2, "Desk", 199.99, "Furniture", true),
Product(3, "Mouse", 29.99, "Electronics", true)
)
val strategies = listOf(
PriceFilter(100.0, 500.0),
CategoryFilter("Electronics")
)
val filteredProducts = products.filterWith(strategies)
注意事项
1. 空安全处理
kotlin
val nullableList: List<Int>? = listOf(1, 2, 3, null, 5)
// 安全调用
val result1 = nullableList?.filter { it != null && it > 2 }
// 使用空安全操作符和 filterNotNull
val result2 = nullableList
?.filterNotNull()
?.filter { it > 2 }
// 提供默认值
val result3 = (nullableList ?: emptyList())
.filterNotNull()
.filter { it > 2 }
2. 性能考虑
kotlin
// 避免在 filter 中进行复杂计算
val largeList = (1..10000).toList()
// 不好:每次过滤都计算平方
val bad = largeList.filter {
val squared = it * it
squared > 1000
}
// 好:预先计算或简化
val good = largeList.filter { it > 31 } // 因为 32² = 1024 > 1000
3. 保持不可变性
kotlin
val original = listOf(1, 2, 3, 4, 5)
// filter 不会修改原始集合
val filtered = original.filter { it > 3 }
println(original) // [1, 2, 3, 4, 5] - 不变
println(filtered) // [4, 5]
总结
filter 函数是 Kotlin 集合处理的核心工具,具有以下特点:
- 多种变体 :提供了
filterNot、filterIndexed、filterIsInstance等多种变体 - 链式调用 :可以与其他集合函数(如
map、flatMap)链式调用 - 惰性求值 :通过
asSequence()支持惰性求值,优化性能 - 类型安全:支持泛型,提供编译时类型检查
- 空安全:与 Kotlin 的空安全系统完美集成
使用建议:
- 对于简单条件,直接使用 lambda 表达式
- 对于复杂或可重用的条件,定义谓词函数
- 处理大数据集时考虑使用序列
- 需要同时获取满足和不满足条件的元素时使用
partition