【问题标题】:Groovy/Grails LinkedHashMap behaving weirdlyGroovy/Grails LinkedHashMap 行为怪异
【发布时间】:2012-05-10 06:03:17
【问题描述】:

我在 LinkedHashMap 中遇到了一些令人困惑的行为 圣杯 2.0.3。在 grails 控制台中运行以下脚本:

def m = ["smart-1":[stuff:'asdf']]
println m.getClass()

def p = [id:1]
println m."smart-$p.id"
println m["smart-$p.id"]
println m.get("smart-$p.id")

println m.'smart-1'
println m['smart-1']
println m.get('smart-1')

给出输出:

class java.util.LinkedHashMap
[stuff:asdf]
[stuff:asdf]
null
[stuff:asdf]
[stuff:asdf]
[stuff:asdf]

在集成测试中,我看到了相反的行为 - 我只能 使用 m.get(GStringImpl) 获取 HashMap 的内容(相反 m.get(String))。

这种行为是预期的还是已知的?

【问题讨论】:

    标签: grails groovy linkedhashmap


    【解决方案1】:

    首先:不要在你的 hashmap 键中使用 GStrings。曾经。您在检索项目时几乎总是会遇到问题,因为 GString is not a String(该页面上的红色框),并且没有相同的哈希值。相反,请使用以下选项之一:

    def key = 'key'
    ['key': value]
    [(key): value]
    [("some $key".toString()): value]
    

    这可确保您在使用字符串时始终获得结果。 (因此,对于查找,也始终使用字符串。)

    我不是 100% 确定您为什么会看到奇怪的行为,但我有一个可靠的猜测。 get() 方法是一种 Java 方法,而数组样式(也可能是属性样式)查找是使用 getAt() 实现的,这是一种 Groovy (GDK) 方法。我的猜测是 Groovy 方法可以识别 GStrings,并默默地处理转换以确保您不会被绊倒。

    最简单的解决方案是始终使用getAt(),而不是get

    def m = ['smart-1':[stuff:'asdf']]
    println m.getClass()
    
    def p = [id:1]
    println m."smart-$p.id"
    println m["smart-$p.id"]
    println m.getAt("smart-$p.id")
    
    println m.'smart-1'
    println m['smart-1']
    println m.getAt('smart-1')
    

    效果很好。

    更好的解决方案是确保在查找值时使用字符串,如下所示:

    println m.get("smart-$p.id".toString())
    

    这也有效。我比较喜欢这个方法,因为直接调用方法的时候,更清楚你的key是String。在使用数组样式或属性样式的访问器时,我仍然会使用普通的 GString,因为那是标准的 Groovy 语法。


    在集成测试中,我看到了相反的行为 - 我只能使用 m.get(GStringImpl) 获取 HashMap 的内容(而不是 m.get(String))。

    这很可能是因为您在 hashmap 中的键是 GString。

    如果 GString 没有任何变量,Groovy 编译器会默默地将其转换为 String 字面量(性能更好),这就是为什么上面的示例实际上使用 String 作为键,但查找使用的是 GString。

    例如

    "Hello $name" -> GString('Hello $name')
    "Hello Bob"   -> 'Hello Bob'
    

    最后的想法:只要你在 groovy 中,就不要使用 get(),因为 Groovy 提供了更简洁的 [] 和属性语法。

    【讨论】:

    • +1 最后的想法:)。我还要注意map['key']map.key 只是map.getAt(key) 的语法糖;是的,如果可能的话,我建议始终使用前两个选项。
    • 感谢您提供如此全面的答案。
    • 正如怀疑的那样,我的应用程序中的映射键是 GStrings。添加了.toString() 调用,现在地图的行为符合预期。谢谢 :¬)
    猜你喜欢
    • 1970-01-01
    • 2011-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-04
    相关资源
    最近更新 更多