过滤 Kotlin 对集合,使对内容不为空
Filter Kotlin collection of pairs so pair content is not null
我正在寻找一种将 List<Pair<T?, U?>>
过滤为 List<Pair<T, U>>
的方法:我想忽略包含 null 的对。
有效,但我也需要智能投射:
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls() =
filter { it.first != null && it.second != null }
有效,但很丑 !!
,而且一点也不符合习惯
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls() = mapNotNull {
if (it.first == null || it.second == null) {
null
} else {
Pair(it.first!!, it.second!!)
}
}
有什么好的解决方案吗?还有更通用的解决方案吗? (也许甚至在 类 中也可以是 deconstructed,比如三元组或数据 类?)
您可以先创建一个将 Pair<T?, U?>
转换为 Pair<T, U>?
的函数。我需要将 first
和 second
放入一些变量中以使智能转换工作。您也可以使用 first?.let { t ->
。
像这样:
fun <T, U> Pair<T?, U?>.toNullablePair() {
val t = first
val u = second
return if (t == null || u == null) null else t to u
}
然后您可以使用它创建您的过滤器函数:
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls() =
mapNotNull { it.toNullablePair() }
不幸的是,我认为对此没有好的答案。
如您所说,filter()
选项给出了一个 Pair<T?, U?>
的列表,其中编译器仍然认为该对的值可以为空,即使我们知道它们不能为空。
mapNotNull()
选项解决了这个问题,但需要创建新的 Pair
对象,这是低效的。 (!!
很丑陋,但我认为这是他们被证明是合理的时代之一。遗憾的是编译器的智能转换不够智能,但我知道总类型推断是一个棘手的问题问题,它必须在某个地方停下来。)
我找到了第三个选项,它解决了这两个问题,但代价是不安全的转换:
fun <T, U> List<Pair<T?, U?>>.filterNotNull()
= mapNotNull{ it.takeIf{ it.first != null && it.second != null } as? Pair<T, U> }
与第二个选项一样,它使用 mapNotNull()
来过滤和转换对;但是,它会进行简单的转换以告诉编译器类型参数不可为空。这 returns a Pair<T, U>
以便编译器知道对值不能为空,并且它避免创建任何新对象(当然除了整个列表)。
但是,它给出了关于不安全转换的编译时警告。它对我来说运行良好 (Kotlin/JVM),但像所有不安全的强制转换一样,不能保证它在所有平台或所有未来版本上都能正常工作。所以并不理想。
这三个选项都有缺点。也许最好的整体是您的第二个选择;代码本身有点难看,而且效率低下(创建新对),但它是安全的,总是有效的,而且很好用。
(顺便说一句,我认为 filterNotNull()
是这个函数更好的名字。在标准库中,过滤器函数似乎是根据它们 保留 ,而不是他们掉落的东西,所以 filterAnyNulls()
会给人完全错误的印象!)
这实际上只是您第二个建议的变体,但另一种选择是显式 return 非空类型:
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls(): List<Pair<T, U>> = this
.filter { (a, b) -> a != null && b != null }
.map { (a, b) -> a!! to b!! }
我希望 contracts 可能是避免 !!
在 map
中大喊大叫的解决方案,但看起来它们仍然非常有限,而且还远远不够发达.我认为目前没有办法避免 !!
,至少在编译器或合约变得更智能之前是这样。
这个解决方案利用了解构和智能转换,并且会做你需要的而不是丑陋的 !!
:
fun <T, U> List<Pair<T?, U?>>.filterPairs(): List<Pair<T, U>> =
mapNotNull { (t, u) ->
if (t == null || u == null) null else t to u
}
我正在寻找一种将 List<Pair<T?, U?>>
过滤为 List<Pair<T, U>>
的方法:我想忽略包含 null 的对。
有效,但我也需要智能投射:
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls() =
filter { it.first != null && it.second != null }
有效,但很丑 !!
,而且一点也不符合习惯
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls() = mapNotNull {
if (it.first == null || it.second == null) {
null
} else {
Pair(it.first!!, it.second!!)
}
}
有什么好的解决方案吗?还有更通用的解决方案吗? (也许甚至在 类 中也可以是 deconstructed,比如三元组或数据 类?)
您可以先创建一个将 Pair<T?, U?>
转换为 Pair<T, U>?
的函数。我需要将 first
和 second
放入一些变量中以使智能转换工作。您也可以使用 first?.let { t ->
。
像这样:
fun <T, U> Pair<T?, U?>.toNullablePair() {
val t = first
val u = second
return if (t == null || u == null) null else t to u
}
然后您可以使用它创建您的过滤器函数:
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls() =
mapNotNull { it.toNullablePair() }
不幸的是,我认为对此没有好的答案。
如您所说,filter()
选项给出了一个 Pair<T?, U?>
的列表,其中编译器仍然认为该对的值可以为空,即使我们知道它们不能为空。
mapNotNull()
选项解决了这个问题,但需要创建新的 Pair
对象,这是低效的。 (!!
很丑陋,但我认为这是他们被证明是合理的时代之一。遗憾的是编译器的智能转换不够智能,但我知道总类型推断是一个棘手的问题问题,它必须在某个地方停下来。)
我找到了第三个选项,它解决了这两个问题,但代价是不安全的转换:
fun <T, U> List<Pair<T?, U?>>.filterNotNull()
= mapNotNull{ it.takeIf{ it.first != null && it.second != null } as? Pair<T, U> }
与第二个选项一样,它使用 mapNotNull()
来过滤和转换对;但是,它会进行简单的转换以告诉编译器类型参数不可为空。这 returns a Pair<T, U>
以便编译器知道对值不能为空,并且它避免创建任何新对象(当然除了整个列表)。
但是,它给出了关于不安全转换的编译时警告。它对我来说运行良好 (Kotlin/JVM),但像所有不安全的强制转换一样,不能保证它在所有平台或所有未来版本上都能正常工作。所以并不理想。
这三个选项都有缺点。也许最好的整体是您的第二个选择;代码本身有点难看,而且效率低下(创建新对),但它是安全的,总是有效的,而且很好用。
(顺便说一句,我认为 filterNotNull()
是这个函数更好的名字。在标准库中,过滤器函数似乎是根据它们 保留 ,而不是他们掉落的东西,所以 filterAnyNulls()
会给人完全错误的印象!)
这实际上只是您第二个建议的变体,但另一种选择是显式 return 非空类型:
fun <T, U> List<Pair<T?, U?>>.filterAnyNulls(): List<Pair<T, U>> = this
.filter { (a, b) -> a != null && b != null }
.map { (a, b) -> a!! to b!! }
我希望 contracts 可能是避免 !!
在 map
中大喊大叫的解决方案,但看起来它们仍然非常有限,而且还远远不够发达.我认为目前没有办法避免 !!
,至少在编译器或合约变得更智能之前是这样。
这个解决方案利用了解构和智能转换,并且会做你需要的而不是丑陋的 !!
:
fun <T, U> List<Pair<T?, U?>>.filterPairs(): List<Pair<T, U>> =
mapNotNull { (t, u) ->
if (t == null || u == null) null else t to u
}