2012-05-10 26 views
4

我遇到从LinkedHashMap的一些容易混淆的行为 Grails的2.0.3。运行下面的脚本Grails的控制台:的Groovy/Grails的LinkedHashMap的行为古怪

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] 

在一个集成测试,我看到相反的行为 - 我只 可以用得到的HashMap的内容m.get(GStringImpl)(相对于 m.get(String))。

此行为是预期的还是已知的?

回答

19

第一:不要在你的HashMap的键使用GString的。永远。您几乎总会遇到检索该项的问题,因为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()) 

也可以工作。我更喜欢这种方法,因为直接调用方法时,键更清晰。在使用数组样式或属性样式访问器时,我仍然使用普通的GString,因为这是标准的Groovy语法。


在一个集成测试,我看到相反的行为 - 我只能得到使用HashMap的m.get(GStringImpl)(相m.get(字符串))的内容。

这很可能是因为你的密钥在你的hashmap中保留了一个GString。

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

例如

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

最后一个想法:只要你是在Groovy中,不使用get(),因为Groovy提供了更清洁[]和财产语法。

+0

+1对于最终的想法:)。我还想指出'map ['key']'和'map.key'只是'map.getAt(key)'的语法糖;是的,如果可能的话,我建议你总是使用这两个前选项。 – epidemian

+0

感谢您的全面回答。 – Alex

+0

可疑的是,我的应用程序中的地图键是GStrings。已经添加了'.toString()'调用,现在地图的行为如预期。感谢:¬) – Alex