0

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

Community
  • 1
  • 1
Meher
  • 2,545
  • 3
  • 26
  • 53
  • Is the state of either user changing during the sort? If user1 > user2 == true, then user2 <= user1 has to be true too, if the sort tries that later. Have you considered splitting this into two comparators? A UserNameComparator and a UserPresenceComparator? – alex Sep 02 '13 at 08:53
  • When my friends presence changes, i ll be notified and sort by alphabetical first and then presence next.This process continues for every new presence notified. Collections.sort(mUsers, new GenericComparator(EnumComparatorObjectType.ENUM_OBJECT_ADDRESS_BOOK_ENTRY)); Collections.sort(mUsers, new GenericComparator(EnumComparatorObjectType.ENUM_OBJECT_PRESENCE)); – Meher Sep 02 '13 at 08:58
  • using two different comparators can solve in my case ? – Meher Sep 02 '13 at 08:59
  • 1
    If I were you, I'd compare getState() first, if the result equals to 0, I'd then compare getUsername(). Otherwise, return the getState() result. – Aprian Sep 02 '13 at 09:17
  • @Aprian the same logic applies in link 2 mentioned,where time = presence and voltot = username.is that same you mean ? – Meher Sep 02 '13 at 09:28
  • @Meher yeah, that's it. Any reason why you don't use that? – Aprian Sep 02 '13 at 09:30
  • I was thinking that if the state of anything changed, ie. the sort mode, then the sort algorithm would get confused and throw this exception. A pair of separate Comparators would prevent that. – alex Sep 02 '13 at 14:06

1 Answers1

0

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);
Thomas Mueller
  • 48,905
  • 14
  • 116
  • 132
  • @421 did you remove the `try { ... } catch ...` part? It needs to be removed as it hides problems. If you do get an exception while comparing, then please post the stack trace of that exception. – Thomas Mueller Sep 02 '13 at 13:11
  • yes,i removed try-catch earlier.i update your latest answer.Thanks – Meher Sep 02 '13 at 16:06
  • Thanks for the code correction as shown.The error also lies in handling the array-list,which keeps on changing as presence changes.Now created one more temp arraylist to sort the list.I could not see the error now. – Meher Sep 04 '13 at 11:31