Reputation: 1995
I have this code. The getCreatedAt is a long representing the timestamp of the record.
Collections.sort(records, new Comparator<Record>() {
@Override
public int compare(Record record1, Record record2) {
return (int) (record2.getCreatedAt() - record1.getCreatedAt());
}
});
When I run It with enough data (roughly 200 items in the list). I start to hit this exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
Of course, I realized when I wrote this code that it could, in theory, have the integer overflow but feel it was unlikely since the timestamps would be days or weeks apart not decades.
I have now fixed this by always returning 1, -1 or 0 but I am curious why this exception even happened in the first place.
Upvotes: 2
Views: 222
Reputation: 58909
I assume the timestamps are long
s, because otherwise you wouldn't need the (int)
cast.
In that case, it's because of overflow. int
can hold values from -2147483648 up to 2147483647. If the subtraction result is bigger or smaller than that, then converting the result to an int
causes it to wrap around.
For example, if record2.getCreatedAt()
is 2147483648, and record1.getCreatedAt()
is 0, then your function returns -2147483648 which indicates that record1 is greater (obviously wrong!).
Now by itself that's okay, it means you'll get things in the wrong order but it shouldn't crash, right? Actually, it can cause a crash.
Let's call the one with 2147483648 record A, and the one with 0 record C. If there was another record B in the middle with a timestamp of say 1000000000, then your function says that C comes after A, A comes after B and B comes after C. It's a loop! There's no way to sort these!
Some sorting algorithms will return a not-properly-sorted list anyway, but the one that Java uses gets totally confused and throws an exception.
Upvotes: 2