0

Possible duplicate: Does this custom compare method contain a logical error
I try to sort location list with a default location. Which position close to default location is greater than other. Here is my try:

public int compare(Location lhs, Location rhs) {
    if(lhs == null && rhs == null){
        Ln.d("l1, l2 are null");
        return 0;
    }

    if(lhs == null && rhs != null){
        Ln.d("l1 is null");
        return -1;
    }

    if(lhs != null && rhs == null){
        Ln.d("l2 is null");
        return 1;
    }

    float[] result1 =  new float[1];
    float[] result2 = new float[1];
    double startLat;
    double startLng;
    double endLat;
    double endLng;
    double defaultLat = getDefaultLocation().getLatitude();
    double defaultLng = getDefaultLocation().getLongitude();

  // because sometime location is null:
    try{
        startLat = Double.parseDouble(lhs.getLat());
        startLng = Double.parseDouble(lhs.getLng());
    }catch(Exception ex){
        return 1;
    }

    try{
        endLat = Double.parseDouble(rhs.getLat());
        endLng = Double.parseDouble(rhs.getLng());
    }catch(Exception ex){
        return -1;
    }

    android.location.Location.distanceBetween(startLat, startLng, defaultLat, defaultLng, result1);         
    android.location.Location.distanceBetween(endLat, endLng, defaultLat, defaultLng, result2);

    int result = Double.compare(result1[0], result2[0]);
    Ln.d("result 1 = " + result1[0] + ", result 2 = " + result2[0] + "  and result = " + result);
    return result;  

}

The problem is it gives me a java.lang.IllegalArgumentException like this:

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

I also search many answer on stackoverflow, but all of them not work for me. I understand method is not transitive but I don't know how to fix it. Any help would be appreciated.

Update: Here is my location entity:

public class Location{
    private long id;        
    private String provider_name;
    private String display_address; 
    private String lat; 
    private String lng;
    private int zoom;   
    private String contact; 
    private String website; 
    private String email;
        // getters & setter
 }

Update 2: Finally it works, because it is my mistake. When the left location lat or long is null, it should be return 1 instead of -1. Here is the right code:

 try{
        startLat = Double.parseDouble(lhs.getLat());
        startLng = Double.parseDouble(lhs.getLng());
    }catch(Exception ex){
        return 1;
    }

    try{
        endLat = Double.parseDouble(rhs.getLat());
        endLng = Double.parseDouble(rhs.getLng());
    }catch(Exception ex){
        return -1;
    }

Many thanks for rolfl and other people.

Community
  • 1
  • 1
ductran
  • 10,043
  • 19
  • 82
  • 165
  • post the declaration of your class. It should give more details regarding the contract of the compare method. – njzk2 Nov 04 '13 at 14:54
  • @njzk2 I have updated my Location class. – ductran Nov 04 '13 at 15:11
  • where is your `compare` method located ? In a comparator implementation ? in which case, can you also post the comparator class declaration ? – njzk2 Nov 04 '13 at 15:14
  • @njzk2 I have already tried google but I don't know where my issue comes from. I have read your link, but I don't know how to make it work for my case – ductran Nov 04 '13 at 15:17
  • your method is not transitive. It means that there are 3 values A, B, and C such as A > B, B > C, but C > A, when transitivity commands that A > C. You have to run in debug mode, look at the values being compared when the exception occurs, and figure out what should be the correct behavior. – njzk2 Nov 04 '13 at 15:19
  • Ok, I figured out my problem as rolfl 's suggestion. Anyway, thanks for your help. – ductran Nov 04 '13 at 15:28

2 Answers2

1

You could also take this approach:

 Collections.sort(yourLocations, new Comparator<Location>() {
                    @Override
                    public int compare(Location o, Location o2) {
                        final Float distance1 = currentLocation.distanceTo(o);
                        final Float distance2 = currentLocation.distanceTo(o2);
                        return distance1.compareTo(distance2);
                    }
                });
Marius M
  • 496
  • 9
  • 16
  • I guess it would be fair-play to motivate your down-vote! – Marius M Nov 04 '13 at 14:58
  • the DV is not mine, but i would guess it is related to `android.location.Location` and `parseDouble` pointing to the direction that `Location` objects in the code are not `android.location.Location`, and therefore don't have `distanceTo` method. – njzk2 Nov 04 '13 at 15:16
  • Okey, I see your point, anyway - as far as the custom Location looks like, it could just as well extend the android Location class with a few extra needed properties and thus getting the above functionality for free. – Marius M Nov 04 '13 at 15:21
  • totally agreed. would be cleaner. – njzk2 Nov 04 '13 at 15:23
  • @Marius M: The down-vote is not mine too, I just up-vote for you. njzk2 is right. I use the object have the same name with android.location.Location. Anyway, thanks for your help. – ductran Nov 04 '13 at 15:32
0

You get this exception when you have two Objects, a and b and you also have the following results for these values from the comparator:

compare(a, b) != 0;
compare(a, b) == compare(b, a);

In this case, there are a couple of conditions where the above is true:

a.getLat() == null && b.getLat() == null

In this case, for example, the compare(a,b) will return 1 (from the exception), and compare(b,a) will also return 1.

hence your issue.

rolfl
  • 17,539
  • 7
  • 42
  • 76
  • Thanks very much. you've made my day, finally I figure out my problem. Because I return wrong value from the exception. I have update my question with the right code. – ductran Nov 04 '13 at 15:27