Search code examples
javacomparecomparatorcompareto

CompareTo is transitive


I have a POJO looking like this:

public class Pojo implements Comparable<Pojo> {

    private String type;

    private String journalId;

    private Date bookingDate;

    private Long account;

    private String description;

    private BigDecimal debit;

    private BigDecimal credit;

    ....
}

and I want to sort a list of these POJOs. Currently my compareTo method looks like this:

@Override
public int compareTo(EfdisJournal other) {
    int i = this.type.compareTo(other.type);
    if (i != 0)
        return i;
    if (this.bookingDate != null && other.bookingDate != null)
        i = this.bookingDate.compareTo(other.bookingDate);
    if (i != 0)
        return i;
    if (this.journalId != null && other.journalId != null)
        i = this.journalId.compareTo(other.journalId);
    if (i != 0)
        return i;
    return this.account.compareTo(other.account);
}

If I run a sort with this compareTo method, I get this java.lang.IllegalArgumentException: Comparison method violates its general contract error. I did google a bit and I think it happens because some of the fields are null on comparison. Yet I have no idea how to solve this or if I am right why that error appears.

The comparison should work like this: 1st compare by type, then compare by bookingDate, as 3rd compare by journalId and at last compare by account. All comparisons should be ascending.

  • type is never null
  • bookingDate may be null
  • journalId may be null
  • account is never null

EDIT:

Sadly I was not able to implement the method, so that the order is as needed. Yet, i solved the problem I had, because the stored procedure yielded 2 resultsets, of which the second was order as needed, so the only thing I had to do was to use the 2nd resultset instead of the first.


Solution

  • You need to deal with the case where one instance has a null bookingDate, and the other has a non-null bookingDate. You should decide whether things with null bookingDate should be sorted before or after things with a non-null bookingDate, and write your compareTo appropriately. (And then journalId too.) Then you can get an order that sorts consistently.

    For instance:

    @Override
    public int compareTo(EfdisJournal other) {
        int i = this.type.compareTo(other.type);
        if (i != 0) {
            return i;
        }
        if ((this.bookingDate==null) ^ (other.bookingDate==null)) {
            return (this.bookingDate==null ? -1 : 1);
        }
        if (this.bookingDate != null && other.bookingDate != null) {
            i = this.bookingDate.compareTo(other.bookingDate);
        }
        if (i != 0) {
            return i;
        }
        if ((this.journalId==null) ^ (other.journalId==null)) {
            return (this.journalId==null ? -1 : 1);
        }
        if (this.journalId != null && other.journalId != null) {
            i = this.journalId.compareTo(other.journalId);
        }
        if (i != 0) {
            return i;
        }
        return this.account.compareTo(other.account);
    }