2012-05-10 24 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