Venator85
Venator85

Reputation: 10335

Sorting with respect to other items - Comparison method violates its general contract

I'm tyring to sort an array of items in which an item should be brought to the beginning of the array if its id equals the id of a selected item (also belonging to the array). The sorting order of the rest of the elements is their distance from a given location.

I'm experiencing the dreaded java.lang.IllegalArgumentException: Comparison method violates its general contract! exception in my custom comparator, implemented like this:

Location location = ...; // may be null
Item selectedItem = ...; // may be null or an element of the array to be sorted

private final Comparator<Item> comparator = new Comparator<Item>() {
    @Override
    public int compare(Item p1, Item p2) {
        // if p1 is the currently selected item, bring p1 to the top
        // if p2 is the currently selected item, bring p2 to the top
        // else sort p1 and p2 by their distance from location
        if (selectedItem != null) {
            if (selectedItem.getId() == p1.getId()) { //id's are int and unique in the array
                return -1;
            } else if (selectedItem.getId() == p2.getId()) {
                return 1;
            }
        }

        if (location != null) { //location is an Android Location class instance
            Float distance1 = location.distanceTo(p1.getLocation());
            Float distance2 = location.distanceTo(p2.getLocation());
            return distance1.compareTo(distance2);
        } else {
            return 0;
        }
    }
};

I don't have an exact sequence to replicate the issue, but all observations of the error so far happen when selectedItem and location are not null (they may be both null).

Any hints?

Thanks

Upvotes: 1

Views: 242

Answers (2)

Edward Falk
Edward Falk

Reputation: 10083

I think there's a hint to be found in the answer to this question: https://stackoverflow.com/a/8327575/338479

There are rules (the "contract") for comparators. One is that an object always compare equal to itself (compare() returns 0). Another is that if A>B and B>C, then A>C. Another is that B>A return the opposite result from A>B.

I'm not seeing any obvious violation to these rules in your comparator, but I'm not privy to the inner workings of your other code. Are there cases where your distanceTo() method could be violating the rules of this contract?

Is there any chance that location or selectedItem could be changing in the middle of the sort?

Upvotes: 0

James Montagne
James Montagne

Reputation: 78690

I believe your issue is in the fact that, when equal to selectedItem, you do not return 0 when the two elements being compared are equal. In this way, comparing x to y will say x is greater, but comparing y to x says y is greater (assuming x and y are both equal to selectedItem). This can't be.

if (p1.getId() == p2.getId()) return 0;

Upvotes: 1

Related Questions