2-2-29 快速掌握Kotlin-过滤函数filter

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 集合处理的核心工具,具有以下特点:

  1. 多种变体 :提供了 filterNotfilterIndexedfilterIsInstance 等多种变体
  2. 链式调用 :可以与其他集合函数(如 mapflatMap)链式调用
  3. 惰性求值 :通过 asSequence() 支持惰性求值,优化性能
  4. 类型安全:支持泛型,提供编译时类型检查
  5. 空安全:与 Kotlin 的空安全系统完美集成

使用建议:

  • 对于简单条件,直接使用 lambda 表达式
  • 对于复杂或可重用的条件,定义谓词函数
  • 处理大数据集时考虑使用序列
  • 需要同时获取满足和不满足条件的元素时使用 partition
相关推荐
成都大菠萝4 小时前
2-2-18 快速掌握Kotlin-扩展属性
android
成都大菠萝4 小时前
2-2-21 快速掌握Kotlin-定义扩展文件
android
成都大菠萝4 小时前
2-2-19 快速掌握Kotlin-可空类型扩展函数
android
成都大菠萝4 小时前
2-2-23 快速掌握Kotlin-apply函数详解
android
2501_916007474 小时前
iOS 证书如何创建,从能生成到能长期使用
android·macos·ios·小程序·uni-app·cocoa·iphone
Just_Paranoid5 小时前
【AOSP】Android Dump 信息快速定位方法
android·adb·framework·service·aosp·dumpsys
帅得不敢出门5 小时前
MTK Android11获取真实wifi mac地址
android·mtk
成都大菠萝5 小时前
2-2-16 快速掌握Kotlin-泛型扩展函数
android
I'm Jie5 小时前
Gradle 多模块依赖集中管理方案,Version Catalogs 详解(Kotlin DSL)
android·java·spring boot·kotlin·gradle·maven