GroovyString hashCode and equals do not evaluate t

2019-09-05 22:31发布

问题:

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

回答1:

LinkedHashMap (the default Map type in Groovy) performs a lookup based on the hashCode of the object.

TreeMap performs a compareTo call on the root of the tree, and goes off down the left or right branch depending on the result of this (and returns the value if key.compareTo( node.key ) returns 0)

As you have seen

println myString.compareTo(myGroovyString)

prints 0. This is why the item is found in the TreeMap, and the different hashCode is why it is not found in the LinkedHashMap

Groovy Strings are not Strings, they are a templating mechanism and a completely different object. This is why you get different results for hashCode -- see the 'GStrings aren't Strings' section on this page