【问题标题】:Is there a way to filter out null Any? values in Kotlin Map?有没有办法过滤掉 null Any? Kotlin Map 中的值?
【发布时间】:2017-02-18 08:17:36
【问题描述】:

我正在尝试考虑一个函数,该函数允许通过应用单个函数的类型推断将Map<String, Any?> 对象视为Map<String,Any>

我对 Kotlin 中的转换函数非常陌生,并尝试了地图上的各种 filterfilterValues filterNot,如下所示:

val input = mapOf(Pair("first",null))
val filtered: Map<String,Any> = input.filter { it.value!=null }

其中任何一个都无法编译

input.filterValues { it!=null }
input.filterNot { it.value==null }
input.filterNot { it.value is Nothing }

我似乎能得到的最接近的是应用多个步骤或出现 Unchecked cast 警告。我原以为将值过滤为!=null 就足够了。我唯一的另一个想法是它是由于泛型?

【问题讨论】:

    标签: collections kotlin


    【解决方案1】:

    过滤器函数返回一个与原始地图具有相同泛型类型的地图。要转换值的类型,您需要从 Any? 映射值?到任何,通过做演员。编译器无法知道您传递给 filter() 的谓词确保过滤后的映射的所有值都是非空的,因此它不能使用类型推断。所以你最好的 et 是使用

    val filtered: Map<String, Any> = map.filterValues { it != null }.mapValues { it -> it.value as Any }
    

    或者定义一个函数在单遍中进行过滤和转换,从而能够使用智能转换:

    fun filterNotNullValues(map: Map<String, Any?>): Map<String, Any> {
        val result = LinkedHashMap<String, Any>()
        for ((key, value) in map) {
            if (value != null) result[key] = value
        }
        return result
    }
    

    【讨论】:

    • 可以简化对mapValues 的调用,还是我们坚持接收Map.Entry?看起来很奇怪,它不仅会提供类型 V 的值并期望返回类型 R 给我,但我认为这会导致迭代条目集不会带来的性能成本。可能是函数名太模糊了?
    • 我刚刚意识到.mapValues { it.value as Any } 实现了相同的结果,并删除了传递已经可以访问的it
    • 我认为这与性能无关。无论 mapper 函数接受一个条目还是一个值都不会改变任何东西:mapValues() 函数仍然需要遍历所有条目。获取一个条目而不仅仅是一个值允许做更多的事情而不仅仅是获取值,因为您可能需要访问键来决定如何转换值。
    • 是的,这很好。实际上,我认为这个实现最好不要删除 Unchecked cast 警告,最后只写了一个扩展函数fun &lt;K,V&gt; Map&lt;K,V?&gt;.filterNullValues() = this.filterValues{it!=null}.mapValues { it.value as V },它似乎通过了我检查过的一个微不足道的测试。
    • 它比删除警告效率低,在这种情况下删除警告是安全的。如果您真的创建了自己的函数,那么按照我在答案中显示的那样实现它会比在地图上执行两次更有效。
    【解决方案2】:

    编译器只是没有执行足够深入的类型分析来推断,例如,input.filterValues { it != null } 从映射中过滤掉 null 值,因此生成的映射应该具有非空值类型。就类型和可空性而言,基本上可以存在具有任意含义的任意谓词。

    没有用于从 stdlib 中的映射中过滤 null 值的特殊情况函数(就像可迭代的 .filterIsInstance&lt;T&gt;() 一样)。因此,您最简单的解决方案是应用未经检查的强制转换,从而告诉编译器您确定没有违反类型安全:

    @Suppress("UNCHECKED_CAST")
    fun <K, V> Map<K, V?>.filterNotNullValues() = filterValues { it != null } as Map<K, V>
    

    另请参阅:another question with a similar problem 关于is-check。

    【讨论】:

    • 我喜欢这个函数,它似乎应该与filterNotNull() 一起用于列表/可迭代对象,尽管我不确定与那些相比如何修改计算成本以应用该函数
    【解决方案3】:

    这不会产生警告kotlin 1.5.30

    listOfNotNull(
            nullableString?.let { "key1" to it },
            nullableString?.let { "key2" to it }
        ).toMap()
    

    【讨论】:

      猜你喜欢
      • 2022-06-19
      • 2011-02-03
      • 2021-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-30
      • 2019-08-08
      • 1970-01-01
      相关资源
      最近更新 更多