Groovy / Grails LinkedHashMap behaves strangely

I am facing some confusing behavior from LinkedHashMap in grails 2.0.3. Running the following console script in the 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 result:

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

In the integration test, I see the opposite behavior - I can only get the contents of the HashMap using m.get(GStringImpl) (as opposed to m.get(String) ).

Is this behavior expected or known?

+4
source share
1 answer

First: do not use GStrings in your hashmap keys. Ever. You will almost always have a problem retrieving the item, because the GString is not a string (the red frame on this page) and does not have the same hash value. Instead, use one of the following options:

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

This ensures that you always get the result when using String. (So, always use String as well for your search.)

I am not 100% sure why you see strange behavior, but I have a hard guess. The get() method is a Java method, and an array type (and probably a property) is getAt() using getAt() , which is a Groovy (GDK) method. I assume that the Groovy method is aware of GStrings and is quietly processing the conversion to make sure you haven't worked.

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 great.

The best solution is to make sure you use Strings when searching for values, for example:

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

Which also works. I like this method better, because when you call the method directly, it is clear that your key is a string. I would still use a regular GString when using elements like array or properties, as this is standard Groovy syntax.


In the integration test, I see the opposite behavior: I can get the contents of the HashMap using m.get (GStringImpl) (unlike m.get (String)).

This is most likely because your key in your hash map remains GString.

If the GString does not have any variables, the Groovy compiler silently converts it to a string literal (higher performance), so the above example actually uses the string as a key, but the search uses GString.

eg.

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

One final thought: as long as you are in groovy, do not use get() , as Groovy provides much stronger syntaxes [] and properties.

+19
source

Source: https://habr.com/ru/post/1411694/


All Articles