Y2theZ
Y2theZ

Reputation: 10412

Does this custom compare method contain a logical error

In an android application, I have a list of locations. I need to sort them based on their distance with the user location. for that I implemented a custom comparator:

Collections.sort(houseList, new Comparator<HouseEntity>()
  {
     @Override
     public int compare(HouseEntity house1, HouseEntity house2)
     {
        if(userLocation == null) return 0;
        return (int) (userLocation.distanceTo(house1.location) - userLocation.distanceTo(house2.location));
     }
  });

It is working well on all the testings I made. However some users had a crash with the following error:

java.lang.IllegalArgumentException: Comparison method violates its general contract!

After reading all the other same issues on SO I concluded that this error appears when there might be an error in the logic (for example we might get a>b and b>a in the same time). But I couldn't find any scenario to replicate that logic error.

What scenario could be causing this error? And how can I solve it?

Thanks a lot for any help

Upvotes: 1

Views: 144

Answers (2)

Vladimir Mironov
Vladimir Mironov

Reputation: 30874

As Peter Lawrey said, you should use Double.compare(x, y) or Float.compare(x, y) instead of casting to int. Here the explanation:

Comparator MUST be transitive, i.e. whenever A == B and B == C, then also A == C. Lets imagine we have three points A, B and C with the distances to the user location 0.2, 0.4 and 1.3.

  1. (int) (0.2 - 0.4) = (int) (-0.2) = 0 => A == B
  2. (int) (0.4 - 1.3) = (int) (-0.9) = 0 => B == C
  3. (int) (0.2 - 1.3) = (int) (-1.1) = -1 => A < C

As you can see the comparator is not transitive.

Upvotes: 1

Andr&#225;s Kerekes
Andr&#225;s Kerekes

Reputation: 1001

If userLocation == null then house1.equals(house2) should give true.

Upvotes: 0

Related Questions