Recently, I was working in our codebase on a bug where I found that looking up a value in a HashMap by String produced the correct result whereas looking up a value by GStringImpl using "${key}" produced an incorrect (null) result. The following is a test I did in the Groovy console:
def myMap = ["testString" : "value"]
def testString = "testString"
println myMap.get("${testString}")
println myMap[testString]
println "${testString}".getClass()
println testString.getClass()
String myString = "test"
def myGroovyString = "${myString}"
println myString.equals(myGroovyString)
println myString.hashCode()
println myGroovyString.hashCode()
println myString.compareTo(myGroovyString)
the output produced was the following:
null
value
class org.codehaus.groovy.runtime.GStringImpl
class java.lang.String
false
3556498
3556535
0
Now, if I change the definition of the map to be an implementation of TreeMap such as the following:
def myMap = ["testString" : "value"] as TreeMap
I get the following result:
value
value
class org.codehaus.groovy.runtime.GStringImpl
class java.lang.String
false
3556498
3556535
0
I understand that the reason why this happens is probably because (I did not look at the implementation of HashMap vs. TreeMap) HashMap looks up a key by hashCode() whereas TreeMap is going to use compareTo(...). My question is why do String and GStringImpl not produce the same hashCode() and not produce a true result when equals(...) is used? Is this a bug/design error? Or was this done for a reason? It seems the results of these methods should be compatible since interactions between these classes are supposed to be seamless to the programmer. The result of this is a huge potential for mistakes in code that might seem intuitive at first but results in a bug in map lookups.
Thanks,
Chris