Search code examples
javaandroidandroid-4.2-jelly-bean

how to avoid IllegalArgumentException while sorting in android


ISSUE link 1 link 2 link 3

From the above links,i hope i'm following the answers which are accepted.But still i m getting the exception. I'm using Java 6.

code :

public class GenericComparator implements Comparator<User> {

private static final String TAG = "java.util.Comparator.GenericComparator";
EnumComparatorObjectType mType;

public GenericComparator(EnumComparatorObjectType paramType) {

    mType = paramType;
}

@Override
public int compare(User user1, User user2) {

    if (user1 == null && user2 == null)
        return 0;
    try {
        if (mType == EnumComparatorObjectType.ENUM_OBJECT_ADDRESS_BOOK_ENTRY) {
            String name1 = user1.getUsername();
            String name2 = user2.getUsername();
            return name1.compareToIgnoreCase(name2);
        } else if (mType == EnumComparatorObjectType.ENUM_OBJECT_PRESENCE) {
            EnumPresence p1 = user1.getState();
            EnumPresence p2 = user2.getState();
            return p1.compareTo(p2);
        }
    } catch (Exception e) {
        Logger.i(TAG, e.getMessage(), e);

    }
    return 0;
}

}

Stack trace :

java.lang.IllegalArgumentException: Comparison method violates its general contract! at java.util.TimSort.mergeHi(TimSort.java:864) at java.util.TimSort.mergeAt(TimSort.java:481) at java.util.TimSort.mergeForceCollapse(TimSort.java:422) at java.util.TimSort.sort(TimSort.java:219) at java.util.TimSort.sort(TimSort.java:169) at java.util.Arrays.sort(Arrays.java:2038) at java.util.Collections.sort(Collections.java:1891) at com.sample.app.adapters.BuddyListAdapter.filerContacts(BuddyListAdapter.java:144) at com.sample.app.adapters.BuddyListAdapter.notifyDataSetChanged(BuddyListAdapter.java:126) at com.sample.app.HomeActivity$2.onReceive(HomeActivity.java:325) at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:763) ... 9 more


Solution

  • You have taken care of the case that both entries are null, but not the case that one is null and the other is not. The problem was hidden by the fact that you used catch (Exception ...), which also catches NullPointerException, and then return 0. That means any object compared to null returns 0 (meaning null and an object are equal), but non-null entries are compared normally. According to this logic, you coult get a[1] == a[2], and a[1] == a[3], but a[2] != a[3]. This is clearly wrong as it wouldn't allow objects to be sorted correctly. This is what the exception message "Comparison method violates its general contract" is trying to say. So I suggest to not catch Exception.

    I suggest to try the following:

    if (user1 == null || user2 == null) {
        if (user1 == user2) {
            // both are null
            return 0;
        } else if (user1 == null) {
            return -1;
        }
        // user2 is null
        return 1;
    }
    if (mType == EnumComparatorObjectType.ENUM_OBJECT_ADDRESS_BOOK_ENTRY) {
        String name1 = user1.getUsername();
        String name2 = user2.getUsername();
        return name1.compareToIgnoreCase(name2);
    } else if (mType == EnumComparatorObjectType.ENUM_OBJECT_PRESENCE) {
        EnumPresence p1 = user1.getState();
        EnumPresence p2 = user2.getState();
        return p1.compareTo(p2);
    } else {
        throw IllegalArgumentException("Unsupported type: " + mType);
    }
    

    If you first want to compare by state and then by name, you could get rid of mType, and the second part (after checking for null) would become:

    EnumPresence p1 = user1.getState();
    EnumPresence p2 = user2.getState();
    int comp = p1.compareTo(p2);
    if (comp != 0) {
        return comp;
    }
    String name1 = user1.getUsername();
    String name2 = user2.getUsername();
    return name1.compareToIgnoreCase(name2);