Odd Behaviour in Groovy when Comparing Maps

Posted by & filed under , .

groovy-langI came across this the other day and I ended up spending quite some time on it pulling my hair and I still couldn’t explain it in the end so I thought  I’d post here to see if anyone can shed some light into it.

In brief, it’s about Groovy’s (otherwise awesome!) feature of comparing maps using == operator: we know that translates to equals() and if we look at the JDK Map.equals() that performs a key/value pair by key/value pair comparison, in other words it’s not a shallow operation based on just the object hash.

This makes it useful when writing unit tests in Groovy because any method that returns a Map I can simply write: resultOfMethod == [some: map] which makes the code much more succinct and easier to understand.

Also, because Groovy has the nice GString implementation and the GString == operator returns true when dealing with String‘s with same characters, this makes Groovy preferable for me, especially for unit tests, where I use the == operator everywhere.

As such I would have thought that the following code would render true for == operator — have a look:

class MapMismatch {
static final String CONSTANT_ONE = 'one'
 
static final String CONSTANT_TWO = 'two'
 
static void main(String []args) {
 Map<String,String> usingConstant = [
  "$CONSTANT_ONE" : '1',
  "$CONSTANT_TWO" : '2'
 ]
 
 Map<String,String> withoutConstant = [
  "one" : '1',
  "two" : '2'
 ]
 
 println usingConstant
 println withoutConstant
 println usingConstant == withoutConstant
 println  withoutConstant == usingConstant
}
}

Except that if you run the above you will see that while the values and the keys in the map have the same String value, the comparison of the maps fails and == reports them as NOT equal!

The only explanation I could muster is that Groovy perhaps delegates entirely the comparison to the JDK which rather than comparing the key values looks at the fact that in one case (usingConstant instance) the implementation of the key is GStringImpl whereas in the other case (withoutConstant) is simply an instance of String — at which point it doesn’t compare the values and simply fails. However that is stupid bearing in mind that "$CONSTANT_ONE" == "one", right? So why would the Groovy guys go for such an implementation — is it intentional or have I found a bug?