Maverick283
Maverick283

Reputation: 1403

IllegalArgumentException: Comparison method violates general contract

I'm sorry, yet another one of those questions, but I can simply not understand what is going on here, or where I'm going wrong.

Overview: I have a ArrayList containing multiple Movie - Objects.Those have a date, and a startingTime. I'd like to sort them by the following conditions, priority as listed:

But when doing so, I get the following error:

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:2010)
    at java.util.Collections.sort(Collections.java:1883)
    at org.pbdevelopement.cineapp.MainActivity.sortByStartingTime(MainActivity.java:208)

What I've got: I have the same thing going on in a JavaFX Project, but there I sort using ArrayList.sort() rather then Collections.sort(). I needed to convert to the Collections.sort() as apparently Android does not support the ArrayList.sort() Method yet.

This is the method that initiates the sorting and defines the Comperator:

private ArrayList<Movie> sortByStartingTime(ArrayList<Movie> list) {
    Collections.sort(list, new Comparator<Movie>() { //This line is MainActivity.java: 208  ;)
        @Override
        public int compare(Movie o1, Movie o2) {
            int comparison = o1.getDate().compareTo(o2.getDate());
            if (comparison == 0) {
                return o1.getStartingTime().compareTo(o2.getStartingTime());
            }
            return comparison;
        }
    });
    return list;
}

As you can see, I compare the two date from the movies, and if they are equal, I continue to compare the starting times. Here are the two compare() Methods from my DateKeeperand TimeKeeper classes (I should maybe admit that they are self written):

public int compareTo(DateKeeper toCompare) {
    Calendar startDate = getAsCalendar();
    Calendar endDate = toCompare.getAsCalendar();
    long diff = endDate.getTimeInMillis() - startDate.getTimeInMillis();//in Milli seconds
    return (int) (diff / (1000 * 60 * 60 * 24));
}

I am not returning either -1,0 or 1 as I would like to use this method for the difference between two dates as well. I've read up on the compare method and found this should not be a problem.

public int compareTo(TimeKeeper toCompare) {
    int toReturn = Integer.compare(this.getValue(), toCompare.getValue());
    return toReturn;
}

getValue() simply returns what is known as MINUTES_OF_DAY.

I don't understand how this violates the method contract. For what I know the Comperatoris transitive (or is it not?) reflexive, and symmetric. So why am I getting this error message?

Upvotes: 0

Views: 361

Answers (1)

Mo1989
Mo1989

Reputation: 2494

Your problem is that you divide by number of milliseconds in the day after you subtract.This may lead to two shows that may be <24 hours apart but on different days to show as on the same day. Try:

public int compareTo(DateKeeper toCompare) {
    Calendar startDate = getAsCalendar();
    Calendar endDate = toCompare.getAsCalendar();
    long diff = (endDate.getTimeInMillis()/ (1000 * 60 * 60 * 24)) - (startDate.getTimeInMillis()/ (1000 * 60 * 60 * 24));//in Milli seconds
    return (int) diff;
}

or set the time on both calendar objects to be midnight

Upvotes: 2

Related Questions