The compareTo contract
The problem is in your compareTo . Here is an excerpt from the documentation :
The sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) must provide sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y .
The 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 error return 1; ? Consider the following scenario:
- Given
Ticket t1, t2 - Given
t1.boeking.compareTo(t2.boeking) == 0 - Provided by
t1.equals(t2) return false - Now we have one of the following:
t1.compareTo(t2) returns 1t2.compareTo(t1) returns 1
The final consequence of a contract violation is compareTo .
Problem fixing
First of all, you should take 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 would be much more appropriate to declare something like this instead:
// improved declaration! uses parameterized Comparable<T> public class Ticket implements Comparable<Ticket>
Now we can write our compareTo(Ticket) (not compareTo(Object) anymore). There are many ways to rewrite this, but here is quite simplified:
@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 another:
@Override public boolean equals(Object o) { return (o instanceof Ticket) && (this.compareTo((Ticket) o) == 0); }
Pay attention to the compareTo structure: it contains several return , but in fact the logic flow is quite readable. Notice also how the priority of the sorting criteria is explicit and easily reordered if you have different priorities.
Related Questions
- What is a raw type and why shouldn't we use it?
- How to sort an array or ArrayList ASC first with x and then with y?
- If a function has only one return statement?