As a relative Java noob, I was baffled to find out the following:
Point.java:
public class Point {
...
public boolean equals(Point other) {
return x == other.x && y == other.y;
}
...
}
Edge.java:
public class Edge {
public final Point a, b;
...
public boolean equals(Edge other) {
return a.equals(other.a) && b.equals(other.b);
}
...
}
main snippet: private Set blockedEdges;
public Program(...) {
...
blockedEdges = new HashSet<Edge>();
for (int i = 0; ...) {
for (int j = 0; ...) {
Point p = new Point(i, j);
for (Point q : p.neighbours()) {
Edge e = new Edge(p, q);
Edge f = new Edge(p, q);
blockedEdges.add(e);
// output for each line is:
// contains e? true; e equals f? true; contains f? false
System.out.println("blocked edge from "+p+"to " + q+
"; contains e? " + blockedEdges.contains(e)+
" e equals f? "+ f.equals(e) +
"; contains f? " + blockedEdges.contains(f));
}
}
}
}
Why is this surprising? Because I checked the documentation before I coded this to rely on equality and it says:
Returns true if this set contains the specified element. More formally, returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e))
This sentence is very clear and it states that nothing more than equality is needed. f.equals(e) returns true as shown in the output. So clearly the set does indeed contain an element e such that o.equals(e), yet contains(o) returns false.
While it is certainly understandable that a hash set also depends on the hash values being the same, this fact is mentioned neither in the docs of HashSet itself, nor is any such possibility mentioned in the docs of Set.
Thus, HashSet doesn't adhere to its specification. This looks like a very serious bug to me. Am I completely on the wrong track here? Or how come behaviour like this is accepted?
You're not overriding
equals
(you're overloading it).equals
need to accept anObject
as argument.Do something like
(and same for
Edge
)It's also important to always override
hashCode
when you're overridingequals
. See for instance Why do I need to override the equals and hashCode methods in Java?Note that this mistake would have been caught by the compile if you had used
@Override
. This is why it's good practice to always use it where possible.