Kotlin集合与空值
我们已经学习了 Kotlin 中的空安全(null safety)。在本节中,我们将讨论如何处理集合中的空值(null),因为集合比其他数据类型更复杂。我们还将讨论如何处理可空元素时常用的便利方法。
集合与空值
可空集合和具有可空元素的非空集合是同一枚硬币的两面。此外,我们还需要认识到空集合和可空集合之间的区别。让我们看看四种情况:
val list = listOf<String>()var nullableList: List<Int>? = listOf<Int>(1, 2, 4, 6)val listWithNullableElements: List<Int?> = listOf<Int?>(1, 2, 4, null, null)var absolutelyNullableList: List<Int?>? = listOf<Int?>(1, 2, 4, null, null)
第一种情况:我们有一个简单的空列表。我们可以像对待常规列表一样处理它,并不需要担心空指针异常(NullPointerException)。这个列表是实际的且非空的,只是为空。
第二种情况:我们有一个可空的列表:这样的列表中的元素不可为空,必须是实际的整数。但变量 nullableList
本身可以为空。在使用可空列表时,我们需要使用安全调用运算符(?.
)、空值合并运算符(?:
)等操作,例如:
val list: List<Int> = nullableList ?: listOf<Int>()
第三种情况:我们有一个具有可空元素的列表。该列表的类型是非空的,但其中的元素可以为空。
val num: Int = listWithNullableElements[1] ?: 150
第四种情况:我们结合了第二种和第三种情况:
val num: Int = absolutelyNullableList?.get(1) ?: 150
基本原则是:如果可以返回一个空集合,最好返回空集合,而不是返回 null
,避免使用可空类型。然而,有时我们确实需要处理可空集合。例如,如果我们声明了一个可以接收值或 null
的变量(var
而不是 val
),那么 null
就相当于“无元素”,“没有答案”或“没有结果”。
从包含空值的序列创建非空集合
有时你会遇到包含空值的元素序列,而你需要使用这些序列创建一个没有空值的集合。在这种情况下,可以使用特定的函数 listOfNotNull()
和 setOfNotNull()
,它们帮助我们删除所有空值并返回默认的只读非空集合。让我们来看一下它是如何工作的:
val list = listOfNotNull(1, null, 50, 404, 42, null, 42, 404) // [1, 50, 404, 42, 42, 404]
val set = setOfNotNull(1, null, 50, 404, 42, null, 42, 404) // [1, 50, 404, 42]
所有空值元素都被从新集合中删除。如果你的元素序列只有空值,这些方法将返回一个空集合(非空!)。记住,如果你需要一个可变集合,可以通过 toMutableList()
或 toMutableSet()
将其转换为可变集合。
可空集合的函数
Kotlin 提供了一些方便的工具来处理具有可空元素的集合:isNullOrEmpty()
、getOrNull()
、firstOrNull()
、lastOrNull()
和 randomOrNull()
。让我们来看看它们!
isNullOrEmpty()
:如果集合为空或为null
,则返回true
。否则返回false
。
val emptySet: Set<Int>? = setOf()
val nullSet: Set<Int>? = null
val set = setOf<Int?>(null, null)println(emptySet.isNullOrEmpty()) // true,因为集合为空
println(nullSet.isNullOrEmpty()) // true,因为集合为 null
println(set.isNullOrEmpty()) // false,因为集合中有两个空值元素
getOrNull()
:返回列表或数组中的一个元素,如果该元素不存在,则返回null
(不能用于Set
)。
val list = listOf(0, 1, 2)
println(list.getOrNull(2)) // 2
println(list.getOrNull(3)) // null,因为这个列表没有第四个元素,索引从 0 开始
你可以使用 list[3]
代替,但这样会引发异常,而 getOrNull()
则会在任何情况下返回一个值。
randomOrNull()
:像getOrNull()
一样,如果集合为空,它返回null
,否则返回一个随机元素。
val list = listOf(0, 1, 2)
val list1 = listOf<Int>()println(list.randomOrNull()) // 返回一个元素
println(list1.randomOrNull()) // null,因为集合为空
firstOrNull()
和 lastOrNull()
:允许我们设置特定的条件。如果集合中至少有一个元素满足条件,它们会返回该元素。
区别是:
-
firstOrNull()
会返回集合中 第一个满足条件的元素。如果没有满足条件的元素,它会返回null
。 -
lastOrNull()
会返回集合中 最后一个满足条件的元素。如果没有满足条件的元素,它也会返回null
。
val list = listOf(0, 1, 1, 2, 5, 7, 6)
val num = list.firstOrNull { it > 3 }
val num1 = list.lastOrNull { it == 1 }
最小值和最大值(可空)
Kotlin 为集合提供了许多方便的比较工具——包括处理可空元素的工具。以下是它们的简介:
-
minOrNull()
/maxOrNull()
:返回集合中的最大或最小元素,如果集合为空,则返回null
。 -
minByOrNull()
/maxByOrNull()
:返回满足条件的最大或最小元素,如果没有符合条件的元素,则返回null
。 -
minOfOrNull()
/maxOfOrNull()
:返回元素特性(如值、大小等)上的最大或最小值,若集合为空则返回null
。 -
minWithOrNull()
/maxWithOrNull()
:返回满足条件的最大或最小元素,指定了compareBy {}
块。 -
minOfWithOrNull()
/maxOfWithOrNull()
:返回符合条件的元素特性上的最大或最小值,指定了compareBy {}
块。
我们这里只提到这些函数,详细的示例和讲解可以参考“集合的聚合操作”一节。
有一点需要注意:这些函数都有没有 “OrNull” 后缀的对应版本。曾几何时,这些“没有 OrNull” 的函数是合法的工具。但从 Kotlin 1.4.0 开始,这些函数(如 min()
、max()
、minBy()
、maxBy()
、minWith()
、maxWith()
)被重命名为 minOrNull()
、maxOrNull()
等,且老版本的函数已标记为废弃。到了 Kotlin 1.7.0,这些废弃的函数重新引入,作为它们各自 “OrNull” 对应版本的非空替代品。这些非空版本返回一个集合元素,或者在集合为空时抛出异常。所以使用时要小心!
结论
我们已经讨论了如何处理具有可空元素的集合以及一些便捷的方法。以下是几个要点:
-
Kotlin 中有可空集合、具有可空元素的集合和空集合,它们是不同的。
-
listOfNotNull()
和setOfNotNull()
函数帮助我们从包含空值的序列中创建非空集合。 -
我们可以检查集合是否为空,或者集合中是否有元素满足某些条件,确保不会抛出异常。
-
我们可以使用比较函数如
minOrNull()
、maxOrNull()
等,选择并显示集合元素或其特性。 -
这些函数有对应的非空版本,它们在集合为空时会抛出异常,而不是返回
null
。