Java.lang.IllegalArgumentException: comparison method violates general contract! java.util.Date

java.lang.IllegalArgumentException: Comparison method violates its general contract! at java.util.TimSort.mergeLo(TimSort.java:747) at java.util.TimSort.mergeAt(TimSort.java:483) at java.util.TimSort.mergeCollapse(TimSort.java:410) at java.util.TimSort.sort(TimSort.java:214) at java.util.TimSort.sort(TimSort.java:173) at java.util.Arrays.sort(Arrays.java:659) at java.util.Collections.sort(Collections.java:217) 

I am sorting the collection based on the following comparator.

 public static Comparator<MyClass> CMP_TIME_DESC = new Comparator<MyClass>() { @Override public int compare(MyClass o1, MyClass o2) { return o2.getOrderSendTime().compareTo(o1.getOrderSendTime()); } }; 

Values ​​are always non-zero. The getOrderSendTime () object has a class java.util.Date.

I understand that this is the consistency of transitivity, and I would suggest that such a class would not have such problems. I searched for open issues, but did not find them in the subject.

Any ideas?

+6
source share
3 answers

I had the same exception and this happened when I had java.util.Date and java.sql.Timestamp objects in the same list / array when sorting running in Java8. (This mix is ​​due to the fact that some objects are loaded from database records with the Timestamp data type, while others are created manually, and objects have only a Date object in them.)

An exception also does not occur every time you sort the same data set, and it seems that it should also have at least 32 of these mixed objects.

If I use the old sorting algorithm, this also does not happen (see, as in Ortomala Lokni's answer).

This also does not happen if you use only java.util.Date objects or only java.sql.Timestamp objects in an array.

So the problem seems to be TimSort in combination with the compareTo methods in java.util.Date and java.sql.Timestamp .

However, he did not pay me to investigate why this is happening, since it is fixed in Java 9!

As a workaround to the release of Java9, and we can upgrade our systems, we manually implemented Comparator , which uses only getTime() . This seems to be normal.

Here is the code that can be used to reproduce the problem:

 import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import org.junit.Test; public class TimSortDateAndTimestampTest { // the same test data with all Dates, all Timestamps, all Strings or all Longs does NOT fail. // only fails with mixed Timestamp and Date objects @Test public void testSortWithTimestampsAndDatesFails() throws Exception { List<Date> dates = new ArrayList<>(); dates.add(new Timestamp(1498621254602L)); dates.add(new Timestamp(1498621254603L)); dates.add(new Timestamp(1498621254603L)); dates.add(new Timestamp(1498621254604L)); dates.add(new Timestamp(1498621254604L)); dates.add(new Timestamp(1498621254605L)); dates.add(new Timestamp(1498621254605L)); dates.add(new Timestamp(1498621254605L)); dates.add(new Timestamp(1498621254605L)); dates.add(new Timestamp(1498621254606L)); dates.add(new Timestamp(1498621254607L)); dates.add(new Date(1498621254605L)); dates.add(new Timestamp(1498621254607L)); dates.add(new Timestamp(1498621254609L)); dates.add(new Date(1498621254603L)); dates.add(new Date(1498621254604L)); dates.add(new Date(1498621254605L)); dates.add(new Date(1498621254605L)); dates.add(new Date(1498621254607L)); dates.add(new Timestamp(1498621254607L)); dates.add(new Date(1498621254608L)); dates.add(new Timestamp(1498621254608L)); dates.add(new Date(1498621254611L)); dates.add(new Timestamp(1498621254612L)); dates.add(new Timestamp(1498621254613L)); dates.add(new Date(1498621254607L)); dates.add(new Timestamp(1498621254607L)); dates.add(new Timestamp(1498621254608L)); dates.add(new Timestamp(1498621254609L)); dates.add(new Timestamp(1498621254611L)); dates.add(new Date(1498621254603L)); dates.add(new Date(1498621254606L)); for (int i = 0; i < 200; i++) { Collections.shuffle(dates); Collections.sort(dates); } } } 

Edit: I removed the expectation of the exception so that you could RIP throw it at startup.

+3
source

Your problem is this: Changing sorting algorithms in Java 7

This is because the default sorting algorithm has been changed from MergeSort to TimSort .

The -Djava.util.Arrays.useLegacyMergeSort=true is to add -Djava.util.Arrays.useLegacyMergeSort=true to the JVM environment.

The best option is to comply with the general comparison contract, but I think that you did not provide enough information in your question for this.

+3
source

Add "-Djava.util.Arrays.useLegacyMergeSort = true" to the VM argument.

0
source

All Articles