Skip to content

过滤集合

过滤是集合处理中最常见的任务之一。 在 Kotlin 中,过滤条件由 谓词 (predicates) 定义 —— 即接收一个集合元素并返回布尔值的 lambda 函数:true 表示给定元素与谓词匹配,false 则表示不匹配。

标准库包含一组扩展函数,让你可以在单次调用中过滤集合。 这些函数不会改变原始集合,因此它们既可用于可变集合也可用于只读集合。 要操作过滤结果,你应该将其赋值给一个变量,或者在过滤后进行链式函数调用。

按谓词过滤

基本的过滤函数是 filter()。 当调用并传入谓词时,filter() 会返回与其匹配的集合元素。 对于 ListSet,结果集合都是 List;对于 Map,结果也是 Map

kotlin
fun main() {
    val numbers = listOf("one", "two", "three", "four")  
    val longerThan3 = numbers.filter { it.length > 3 }
    println(longerThan3)

    val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
    val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10}
    println(filteredMap)
}

filter() 中的谓词只能检查元素的值。 如果你想在过滤器中使用元素位置,请使用 filterIndexed()。 它接收一个带有两个实参的谓词:元素的索引和值。

要根据否定条件过滤集合,请使用 filterNot()。 它返回一个列表,其中包含谓词返回 false 的元素。

kotlin
fun main() {
    val numbers = listOf("one", "two", "three", "four")
    
    val filteredIdx = numbers.filterIndexed { index, s -> (index != 0) && (s.length < 5)  }
    val filteredNot = numbers.filterNot { it.length <= 3 }

    println(filteredIdx)
    println(filteredNot)
}

还有一些函数可以通过过滤给定类型的元素来缩小元素类型:

  • filterIsInstance() 返回给定类型的集合元素。 在 List<Any> 上调用时,filterIsInstance<T>() 返回一个 List<T>,从而允许你对其项调用 T 类型的函数。

    kotlin
    fun main() {
        val numbers = listOf(null, 1, "two", 3.0, "four")
        println("All String elements in upper case:")
        numbers.filterIsInstance<String>().forEach {
            println(it.uppercase())
        }
    }
  • filterNotNull() 返回所有非 null 元素。 在 List<T?> 上调用时,filterNotNull() 返回一个 List<T: Any>,从而允许你将这些元素视为不可为 null 的对象。

    kotlin
    fun main() {
        val numbers = listOf(null, "one", "two", null)
        numbers.filterNotNull().forEach {
            println(it.length)   // length 对于可为 null 的 String 是不可用的
        }
    }

分区

另一个过滤函数 —— partition() —— 通过谓词过滤集合,并将不匹配的元素保留在一个单独的列表中。 因此,你会得到一个由 List 组成的 Pair (对) 作为返回值:第一个列表包含与谓词匹配的元素,第二个列表包含原始集合中的其他所有元素。

kotlin
fun main() {
    val numbers = listOf("one", "two", "three", "four")
    val (match, rest) = numbers.partition { it.length > 3 }

    println(match)
    println(rest)
}

测试谓词

最后,还有一些函数只是简单地针对集合元素测试谓词:

  • any() 如果至少有一个元素与给定谓词匹配,则返回 true
  • none() 如果没有元素与给定谓词匹配,则返回 true
  • all() 如果所有元素都与给定谓词匹配,则返回 true。 请注意,在空集合上使用任何有效的谓词调用 all() 都会返回 true。这种行为在逻辑学中被称为 空真 (vacuous truth)。
kotlin
fun main() {
    val numbers = listOf("one", "two", "three", "four")

    println(numbers.any { it.endsWith("e") })
    println(numbers.none { it.endsWith("a") })
    println(numbers.all { it.endsWith("e") })

    println(emptyList<Int>().all { it > 5 })   // 空真
}

any()none() 也可以在不传入谓词的情况下使用:在这种情况下,它们只是检查集合是否为空。 如果有元素,any() 返回 true,否则返回 falsenone() 则相反。

kotlin
fun main() {
    val numbers = listOf("one", "two", "three", "four")
    val empty = emptyList<String>()

    println(numbers.any())
    println(empty.any())
    
    println(numbers.none())
    println(empty.none())
}