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

Во-первых: не используйте GStrings в ключах hashmap. Когда-либо. У вас почти всегда будет проблема с возвратом элемента, потому что GString is not a String (красная рамка на этой странице) и не имеет того же значения хэш-функции. Вместо этого используйте один из следующих вариантов:

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

Это гарантирует, что вы всегда получите результат при использовании строки. (Итак, для поиска всегда используйте также String).

Я не уверен на 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 (String)).

Это, скорее всего, из-за того, что ваш ключ в вашем hashmap остается GString.

Если GString не имеет каких-либо переменных, компилятор Groovy бесшумно преобразует его в строковый литерал (более высокая производительность), поэтому приведенный выше пример фактически использует String в качестве ключа, но поиск использует GString ,

например.

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

Последняя мысль: До тех пор, пока вы находитесь в заводной, не используйте get(), так как Groovy обеспечивает гораздо более чистых [] и недвижимость синтаксисом.

+0

+1 Для окончательной мысли :). Я также хотел бы отметить, что 'map ['key']' и 'map.key' являются просто синтаксическим сахаром для' map.getAt (key) '; и да, я бы рекомендовал всегда использовать эти два предыдущих варианта, если это возможно. – epidemian

+0

Спасибо за такой исчерпывающий ответ. – Alex

+0

Как и следовало ожидать, в моем приложении были клавиши карты GStrings. Добавили '.toString()' вызовы, и теперь карты ведут себя как ожидалось. Спасибо: ¬) – Alex

Смежные вопросы