9.1.1.7 过滤集合

原文链接: https://kotlinlang.org/docs/collection-filtering.html

9.1.1.7 过滤集合

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

标准库中有一组扩展函数,让你可以在一次调用中过滤集合。这些函数不会改变原集合,因此对可变集合和只读集合都可用。要对过滤结果进行操作,你应当把结果赋给变量,或在过滤之后继续链式调用函数。

按谓词过滤

最基本的过滤函数是 filter()。传入谓词后,filter() 会返回匹配该谓词的集合元素。对于 List 和 Set,结果集合是 List;对于 Map,结果同样是 Map。

1
2
3
4
5
6
7
8
9
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 的元素列表。

1
2
3
4
5
6
7
8
9
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 类型的函数。
1
2
3
4
5
6
7
    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() 返回所有非空元素。在 List<T?> 上调用时,filterNotNull() 返回 List<T: Any>,因此你可以把这些元素当作非空对象处理。
1
2
3
4
5
6
    fun main() {
        val numbers = listOf(null, "one", "two", null)
        numbers.filterNotNull().forEach {
            println(it.length) // 可空字符串无法使用 length
        }
    }

划分

另一个过滤函数——partition()——会按谓词过滤集合,并把不匹配的元素保存在另一个列表中。因此返回值是一对 List 组成的 Pair:第一个列表包含匹配谓词的元素,第二个列表包含原集合中其余的所有元素。

1
2
3
4
5
6
7
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。这种行为在逻辑学中称为*空真*。
1
2
3
4
5
6
7
8
9
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,没有元素时返回 false;none() 则相反。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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())
}