Kotlin集合与空值

我们已经学习了 Kotlin 中的空安全(null safety)。在本节中,我们将讨论如何处理集合中的空值(null),因为集合比其他数据类型更复杂。我们还将讨论如何处理可空元素时常用的便利方法。

集合与空值

可空集合和具有可空元素的非空集合是同一枚硬币的两面。此外,我们还需要认识到空集合和可空集合之间的区别。让我们看看四种情况:

kotlin 复制代码
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 本身可以为空。在使用可空列表时,我们需要使用安全调用运算符(?.)、空值合并运算符(?:)等操作,例如:

kotlin 复制代码
val list: List<Int> = nullableList ?: listOf<Int>()

第三种情况:我们有一个具有可空元素的列表。该列表的类型是非空的,但其中的元素可以为空。

kotlin 复制代码
val num: Int = listWithNullableElements[1] ?: 150

第四种情况:我们结合了第二种和第三种情况:

kotlin 复制代码
val num: Int = absolutelyNullableList?.get(1) ?: 150

基本原则是:如果可以返回一个空集合,最好返回空集合,而不是返回 null,避免使用可空类型。然而,有时我们确实需要处理可空集合。例如,如果我们声明了一个可以接收值或 null 的变量(var 而不是 val),那么 null 就相当于"无元素","没有答案"或"没有结果"。

从包含空值的序列创建非空集合

有时你会遇到包含空值的元素序列,而你需要使用这些序列创建一个没有空值的集合。在这种情况下,可以使用特定的函数 listOfNotNull()setOfNotNull(),它们帮助我们删除所有空值并返回默认的只读非空集合。让我们来看一下它是如何工作的:

kotlin 复制代码
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
kotlin 复制代码
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)。
kotlin 复制代码
val list = listOf(0, 1, 2)
println(list.getOrNull(2)) // 2
println(list.getOrNull(3)) // null,因为这个列表没有第四个元素,索引从 0 开始

你可以使用 list[3] 代替,但这样会引发异常,而 getOrNull() 则会在任何情况下返回一个值。

  • randomOrNull() :像 getOrNull() 一样,如果集合为空,它返回 null,否则返回一个随机元素。
kotlin 复制代码
val list = listOf(0, 1, 2)
val list1 = listOf<Int>()

println(list.randomOrNull()) // 返回一个元素
println(list1.randomOrNull()) // null,因为集合为空

firstOrNull()lastOrNull() :允许我们设置特定的条件。如果集合中至少有一个元素满足条件,它们会返回该元素。

区别是:

  • firstOrNull() 会返回集合中 第一个满足条件的元素。如果没有满足条件的元素,它会返回 null

  • lastOrNull() 会返回集合中 最后一个满足条件的元素。如果没有满足条件的元素,它也会返回 null

kotlin 复制代码
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

相关推荐
MediaTea28 分钟前
Python 库手册:keyword 关键字查询
开发语言·python
睿思达DBA_WGX34 分钟前
使用 python-docx 库操作 word 文档(1):文件操作
开发语言·python·word
人工干智能6 小时前
科普:Python 中,字典的“动态创建键”特性
开发语言·python
初听于你7 小时前
缓存技术揭秘
java·运维·服务器·开发语言·spring·缓存
长路归期无望9 小时前
C语言小白实现多功能计算器的艰难历程
c语言·开发语言·数据结构·笔记·学习·算法
是大强10 小时前
stm32摇杆adc数据分析
开发语言
奥尔特星云大使10 小时前
MySQL 慢查询日志slow query log
android·数据库·mysql·adb·慢日志·slow query log
蓝莓味的口香糖10 小时前
【JS】什么是单例模式
开发语言·javascript·单例模式
linux kernel11 小时前
第二十三讲:特殊类和类型转换
开发语言·c++
笨蛋少年派11 小时前
JAVA基础语法
java·开发语言