如何验证列表中没有空元素,Kotlin

igsr9ssn  于 2022-12-30  发布在  Kotlin
关注(0)|答案(4)|浏览(217)

我想检查List<Int?>是否存在空值,如果不存在空值,则允许List作为List<Int>继续,否则使用emptyList()
这是我想到的

var noNullElements: List<Int> = emptyList()
if (listWithPossibleNullElements.all { it != null }) {
    noNullElements = listWithPossibleNullElements as List<Int>
}
//do something with noNullElements list

如果这是最好的,最地道的方式,我会感到震惊。

    • 解决方案**

下面有许多可接受的解决方案,但我选择的是这些解决方案的组合

fun getSafeList(list: List<Int?>): List<Int> =if (null in list) emptyList() else list.requireNoNulls()
kpbpu008

kpbpu0081#

使用contains检查是否存在至少一个 null 值:

val noNullElements = if (list.contains(null)) emptyList() else list.map { it!! }

编辑:添加此扩展功能:

fun List<Int?>.castToIntOrEmpty() = 
  if (this.contains(null)) emptyList() else this.map { it as Int }

val noNullElements = list.castToIntOrEmpty()

没有 null 元素的示例:

val list: List<Int?> = List(10) { it }
// list is: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

val noNullElements = if (list.contains(null)) emptyList() else list.map { it!! }

println(noNullElements)   // Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

包含部分(或全部)null 元素的示例:

val list: List<Int?> = List(10) { if (it % 2 == 0) it else null }
// list is: [0, null, 2, null, 4, null, 6, null, 8, null]

val nullElements = if (list.contains(null)) emptyList() else list.map { it!! }

println(nullElements)   // Output: []
pkmbmrz7

pkmbmrz72#

下面是一个一行扩展函数:

inline fun <reified T : Any> Iterable<T?>.filterNoneNull(): Iterable<T>? =
  takeIf { null !in it }?.requireNoNulls()
  • takeIf {}-一个作用域函数,如果lambda的值为false,则返回null
  • null !in it-使用in运算符函数确定列表是否包含null
  • 最后,如果takeIf {}返回一个非空值,filterNotNull()将把元素类型从可空T?转换为非空T

注意,如果提供的Iterable<T?>包含null,它将返回null,否则返回List<T>,以防提供的列表是空的还是包含null
示例用法-run in Kotlin Playground

fun main() {
  val listA: List<Int?> = listOf(1, 2, 3, null)

  // since listA contains 'null', the result is 'null'
  println(listA.filterNoneNull())
  // prints: null

  // provide an alternative, using the elvis operator
  println(listA.filterNoneNull() ?: emptyList<Int>())
  // prints: []

  val listB: List<Int> = listOf(1, 2, 3)

  // since listB does not contain null, the same list is returned
  println(listB.filterNoneNull())
  // prints: [1, 2, 3]
}

inline fun <reified T : Any> Iterable<T?>.filterNoneNull(): Iterable<T>? =
  takeIf { null !in it }?.requireNoNulls()

注意:我用requireNoNulls()替换了filterNotNull(),因为后者不会示例化新列表

jpfvwuh4

jpfvwuh43#

我认为你的问题是你想把一个泛型的List<Int?>类型当作List<Int>来处理,这意味着要强制转换它。因为List<Int?>不是List<Int>的子类型(尽管相反的情况也是正确的,正如 Joffrey 在注解中指出的),在这种情况下你基本上必须执行一个未检查的强制转换:

if (list.contains(null)) emptyList else list as List<Int>

编译器不够聪明,无法在运行时确定这是一个安全的强制转换(你必须自己保证,例如,确保在检查通过后列表不会被修改为包含空值),所以你会得到一个 unchecked cast 警告。但是如果你真的想传入一个List<Int?>并把它当作一个List<Int>,你就必须隐藏这个警告:
通过使用@Suppress("UNCHECKED_CAST")注解出现未检查的强制转换警告的语句或声明,可以取消该警告
我认为更 * 惯用 * 的方法是创建一个新的List<Int>,而不是尝试将原始的List<Int>强制转换为不同的泛型类型,或者让原始的列表提供程序传递一个List<Int>?(即如果有任何非空元素,则传递 null),这可能只是移动了问题-但这实际上取决于您在做什么。
您可以轻松地创建过滤后的副本,并将其与原始副本进行比较以决定是否使用它,或者编写一个for循环来构建一个新列表,如果遇到null,该列表将短路,诸如此类的事情。(包括一些抛出异常的方法,如requireNoNulls()),因此您有很多选择,并且有不同的权衡-这实际上取决于您正在做什么以及瓶颈在哪里(如果有的话)。当你使用泛型时,事情可能会变得有点笨拙,所以有些情况看起来并不像你希望的那样整洁!

ctehm74n

ctehm74n4#

尝试使用filterNotNull

val noNullElements: List<Int> = listWithPossibleNullElements.filterNotNull()

相关问题