【问题标题】:groovy: safely find a key in a map and return its valuegroovy:安全地在地图中找到一个键并返回它的值
【发布时间】:2012-12-21 05:05:23
【问题描述】:

我想在给定的地图中找到一个特定的键。如果找到键,我想从地图中获取该键的值。

这是我目前所管理的:

def mymap = [name:"Gromit", likes:"cheese", id:1234]

def x = mymap.find{ it.key == "likes" }

if(x)
    println x.value

这行得通,输出如预期的那样是“奶酪”。很好,但我不想在最后做x.value,我也不想做if(x)。我希望 x 以某种方式直接包含该值。

我无法像这样将值直接放入 x 中:

def mymap = [name:"Gromit", likes:"cheese", id:1234]

def x = mymap.find{ it.key == "likesZZZ" }.value

println x

因为在这种情况下 find 闭包为空,这会导致空指针异常。当然,上面的代码sn -p在it.key == "likes"时有效,但我不确定我是否总能在map中找到目标键。

什么是在地图上执行此操作的“Groovier”和安全方式:

  • 检查地图是否有给定的键,
  • 如果是,则获取此键的值

【问题讨论】:

    标签: dictionary groovy closures


    【解决方案1】:

    您忘记提及当密钥不存在时会发生什么。我假设null 没问题。


    可以使用property notation 查询 Groovy 地图。所以你可以这样做:

    def x = mymap.likes
    

    在这种情况下,x 将包含 mymap['likes'] 的值(如果存在),否则为 null。

    如果您要查找的键(例如'likes.key')本身包含一个点,那么您可以像这样引用该键:

    def x = mymap.'likes.key'
    

    如果密钥存储在变量中,请使用带有 属性表示法string interpolation,如下所示:

    def key = 'likes'
    def x = mymap."$key"
    

    如果你不喜欢null,那么使用你使用elvis operator来分配一个默认值来代替(类似于java.util.Map.getOrDefault):

    def x = mymap.someKey ?: 'default value'
    

    现在你知道所有的秘密了。

    请记住,groovy 映射仍然只是带有“enhancements”的 Java 映射,因此适用于 Java 的规则仍然适用于 groovy。值得注意的是,java.util.Map 界面中有一条注释,上面写着:

    一些地图实现对它们可能包含的键和值有限制。例如,一些实现禁止空键和值,而一些实现对其键的类型有限制。尝试插入不合格的键或值会引发未经检查的异常,通常是NullPointerExceptionClassCastException。尝试查询是否存在不合格的键或值可能会引发异常,或者它可能只是返回 false;一些实现会表现出前一种行为,而另一些会表现出后者。更一般地,尝试对不合格的键或值进行操作,其完成不会导致将不合格的元素插入到映射中,这可能会引发异常,也可能会成功,这取决于实现的选择。此类异常在此接口的规范中被标记为“可选”。

    基本上这就是说:并非所有地图都是相同的,因此在尝试对它们进行这些操作时,请确保您对(内部)正在处理的地图类型有所了解。这对于简单的东西应该不是问题,因为 Groovy 默认使用 java.util.LinkedHashMap

    【讨论】:

      【解决方案2】:

      一般来说,这取决于您的地图所包含的内容。如果它有空值,事情就会变得棘手,应该使用containsKey(key)get(key, default) 来检测元素是否真的存在。在很多情况下,代码可以变得更简单,您可以定义一个默认值:

      def mymap = [name:"Gromit", likes:"cheese", id:1234]
      def x1 = mymap.get('likes', '[nothing specified]')
      println "x value: ${x}" }
      

      还要注意containsKey()get() 比设置闭包检查元素mymap.find{ it.key == "likes" } 要快得多。只有当你真的在那里做了更复杂的事情时,使用闭包才有意义。你可以例如这样做:

      mymap.find{ // "it" is the default parameter
        if (it.key != "likes") return false
        println "x value: ${it.value}" 
        return true // stop searching
      }
      

      或者带有显式参数:

      mymap.find{ key,value ->
        (key != "likes")  return false
        println "x value: ${value}" 
        return true // stop searching
      }
      

      【讨论】:

        【解决方案3】:
        def mymap = [name:"Gromit", id:1234]
        def x = mymap.find{ it.key == "likes" }?.value
        if(x)
            println "x value: ${x}"
        
        println x.getClass().name
        

        ?. 检查 null 并且不会在 Groovy 中创建异常。如果键不存在,结果将是org.codehaus.groovy.runtime.NullObject

        【讨论】:

        • 太棒了。这比我选择的原始答案更时髦,更符合我的要求。标记这是答案。谢谢。
        • 使用闭包非常昂贵。最好在那里使用def x = mymap.get("likes")?.value,效率更高
        • 接近我的情况所需,我使用了findAll { it.key in list }
        【解决方案4】:

        您得到空指针异常的原因是因为在您的第二个示例中没有键 likesZZZ。试试:

        def mymap = [name:"Gromit", likes:"cheese", id:1234]
        def x = mymap.find{ it.key == "likes" }.value
        if(x)
            println "x value: ${x}"
        

        【讨论】:

        • 如问题所述,我不能确定钥匙是否始终在地图中,所以我想要一种安全工作的方法(无例外),即使钥匙在或不在地图。
        【解决方案5】:

        使用地图的重点在于直接访问。如果您确定地图中的值永远不会是 Groovy-false,那么您可以这样做:

        def mymap = [name:"Gromit", likes:"cheese", id:1234]
        def key = "likes"
        
        if(mymap[key]) {
            println mymap[key]
        }
        

        但是,如果该值可能是 Groovy-false,您应该使用:

        if(mymap.containsKey(key)) {
            println mymap[key]
        }
        

        不过,如果您知道该值不会是 Groovy-false(或者您可以忽略它)并且想要一个默认值,那么最简单的解决方案是这样的:

        def value = mymap[key] ?: "default"
        

        所有这三个解决方案都比您的示例快得多,因为它们不会扫描整个地图以查找键。他们利用了HashMap(或LinkedHashMap)设计,使直接密钥访问几乎瞬间完成。

        【讨论】:

        • 如果需要默认值,可以将映射定义为def map = [:].withDefault {"Default"}。那么你就不需要使用 Elvis Operator 作为默认值了。当然,这也会更新地图,所以如果你不想这样,那么猫王就是你的方式。
        • Key 也可以用双引号引起来,例如 def headers = ["Content-Type":"application/json"]
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-16
        • 2021-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多