Guillaume
Guillaume

Reputation: 5557

Map with custom key in Groovy vs Java

I want to use a map in Groovy where the keys will be instances of an unmutable class.

This is something I do often in Java and it works fine, like in this example class:

public class TestMap {
    static final class Point {
        final int x; final int y;
        public Point(int x, int y) {this.x = x;this.y = y;}
    }

    public static void main(String[] args) {
        Map<Point, String> map = new HashMap<>();
        final Point origin = new Point(0, 0);
        map.put(origin, "hello world !" );
        if(!map.containsKey(origin))
            throw new RuntimeException("can't find key origin in the map");
        if(!map.containsKey(new Point(0,0))) {
            throw new RuntimeException("can't find new key(0,0) in the map");
        }
    }
}

But when I try to achieve the same thing with Groovy, it doesn't work. Why ? Here is a sample non working example in Groovy:

class Point { 
    final int x; final int y
    Point(int x, int y) { this.x = x; this.y = y }
    public String toString() { return "{x=$x, y=$y}" }
}

def origin = new Point(0, 0)
def map = [(origin): "hello"] 
map[(new Point(1,1))] = "world"
map.put(new Point(2,2),  "!")

assert map.containsKey(origin) // this works: when it's the same ref
assert map.containsKey(new Point(0,0))
assert map.containsKey(new Point(1,1))
assert map.containsKey(new Point(2,2))
assert !map.containsKey(new Point(3,3))

Upvotes: 0

Views: 124

Answers (1)

tim_yates
tim_yates

Reputation: 171084

You need to have an equals and hashCode method on your Point class so that the instances can be found as keys in the HashMap

You can do this quickly by adding an annotation in Groovy:

import groovy.transform.*

@EqualsAndHashCode
class Point { 
    final int x; final int y
    Point(int x, int y) { this.x = x; this.y = y }
    public String toString() { return "{x=$x, y=$y}" }
}

Upvotes: 5

Related Questions