I'm running into some confusing behaviour from LinkedHashMap in grails 2.0.3. Running the following script in grails console:
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')
gives the output:
class java.util.LinkedHashMap
[stuff:asdf]
[stuff:asdf]
null
[stuff:asdf]
[stuff:asdf]
[stuff:asdf]
In an integration test, I'm seeing the opposite behaviour - I can only
get the content of the HashMap using m.get(GStringImpl)
(as opposed
m.get(String)
).
Is this behaviour expected or known?
First: don't use GStrings in your hashmap keys. Ever. You will almost always have an issue retrieving the item, because a GString is not a String (red box on that page), and does not have the same hash value. Instead, use one of these options:
def key = 'key'
['key': value]
[(key): value]
[("some $key".toString()): value]
This ensures that you will always get the result when using a String. (So, for lookups, always use a String, too.)
I'm not 100% sure why you are seeing the odd behavior, but I have a solid guess. The get()
method is a Java method, while the array-style (and probably property-style) lookup is implemented using getAt()
, which is a Groovy (GDK) method. My guess is that the Groovy method is aware of GStrings, and silently handles the conversion to make sure you don't get tripped up.
The simplest solution is to always use getAt()
, not 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')
Which works fine.
The better solution is to ensure that you are using Strings when looking up values, like so:
println m.get("smart-$p.id".toString())
Which also works. I like this method better, because when calling the method directly, it's clearer that your key is a String. I'd still use the normal GString when using the array-style or property-style accessors, because that's standard Groovy syntax.
In an integration test, I'm seeing the opposite behaviour - I can only get the content of the HashMap using m.get(GStringImpl) (as opposed m.get(String)).
This is most likely because your key in your hashmap is staying a GString.
If a GString does not have any variables, the Groovy compiler silently converts it to a String literal (better performance), which is why the above example is actually using a String as the key, but the lookup is using a GString.
e.g.
"Hello $name" -> GString('Hello $name')
"Hello Bob" -> 'Hello Bob'
A final thought: As long as you are in groovy, don't use get()
, since Groovy provides the much cleaner []
and property syntaxes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With