So I\'ve been struggling with a problem for a while now, figured I might as well ask for help here.
I\'m adding Ticket objects to a TreeSet, Ticket implements Comparable
This could happen if your compareTo method isn't consistent. I.e. if a.compareTo(b) > 0
, then b.compareTo(a)
must be < 0. And if a.compareTo(b) > 0
and b.compareTo(c) > 0
, then a.compareTo(c)
must be > 0. If those aren't true, TreeSet can get all confused.
Firstly, if you are using a TreeSet
, the actual behavior of your hashCode
methods won't affect the results. TreeSet
does not rely on hashing.
Really we need to see more code; e.g. the actual implementations of the equals
and compareTo
methods, and the code that instantiates the TreeSet
.
However, if I was to guess, it would be that you have overloaded the equals
method by declaring it with the signature boolean equals(Ticket other)
. That would lead to the behavior that you are seeing. To get the required behavior, you must override the method; e.g.
@Override
public boolean equals(Object other) { ...
(It is a good idea to put in the @Override
annotation to make it clear that the method overrides a method in the superclass, or implements a method in an interface. If your method isn't actually an override, then you'll get a compilation error ... which would be a good thing.)
EDIT
Based on the code that you have added to the question, the problem is not overload vs override. (As I said, I was only guessing ...)
It is most likely that the compareTo
and equals
are incorrect. It is still not entirely clear exactly where the bug is because the semantics of both methods depends on the compareTo
and equals
methods of the Boeking
class.
The first if statement of the Ticket.compareTo
looks highly suspicious. It looks like the return 1;
could cause t1.compareTo(t2)
and t2.compareTo(t1)
to both return 1
for some tickets t1
and t2
... and that would definitely be wrong.
compareTo
contractThe problem is in your compareTo
. Here's an excerpt from the documentation:
Implementor must ensure
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
for allx
andy
.
Your original code is reproduced here for reference:
// original compareTo implementation with bug marked
@Override
public int compareTo(Object o) {
int output = 0;
if (boeking.compareTo(((Ticket) o).getBoeking())==0)
{
if(this.equals(o))
{
return output;
}
else return 1; // BUG!!!! See explanation below!
}
else output = boeking.compareTo(((Ticket) o).getBoeking());
return output;
}
Why is the return 1;
a bug? Consider the following scenario:
Ticket t1, t2
t1.boeking.compareTo(t2.boeking) == 0
t1.equals(t2)
return false
t1.compareTo(t2)
returns 1
t2.compareTo(t1)
returns 1
That last consequence is a violation of the compareTo
contract.
First and foremost, you should have taken advantage of the fact that Comparable<T>
is a parameterizable generic type. That is, instead of:
// original declaration; uses raw type!
public class Ticket implements Comparable
it'd be much more appropriate to instead declare something like this:
// improved declaration! uses parameterized Comparable<T>
public class Ticket implements Comparable<Ticket>
Now we can write our compareTo(Ticket)
(no longer compareTo(Object)
). There are many ways to rewrite this, but here's a rather simplistic one that works:
@Override public int compareTo(Ticket t) {
int v;
v = this.boeking.compareTo(t.boeking);
if (v != 0) return v;
v = compareInt(this.rijNr, t.rijNr);
if (v != 0) return v;
v = compareInt(this.stoelNr, t.stoelNr);
if (v != 0) return v;
v = compareInt(this.ticketType, t.ticketType);
if (v != 0) return v;
return 0;
}
private static int compareInt(int i1, int i2) {
if (i1 < i2) {
return -1;
} else if (i1 > i2) {
return +1;
} else {
return 0;
}
}
Now we can also define equals(Object)
in terms of compareTo(Ticket)
instead of the other way around:
@Override public boolean equals(Object o) {
return (o instanceof Ticket) && (this.compareTo((Ticket) o) == 0);
}
Note the structure of the compareTo
: it has multiple return
statements, but in fact, the flow of logic is quite readable. Note also how the priority of the sorting criteria is explicit, and easily reorderable should you have different priorities in mind.