问题
I have comparator for strings, which are converted to date. When I pass this comparator to Collections.sort() method I get java.lang.IllegalArgumentException: Comparison method violates its general contract!.
I have read some articles about this exception, but I don't really understand why this exception appears. Any idea ?
private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if (o1 == null && o2 == null) {
return 0;
}
if (o1 == null) {
return 1;
}
if (o2 == null) {
return -1;
}
try {
Date first = sdf.parse(o1);
Date second = sdf.parse(o2);
return first.compareTo(second);
} catch (Exception ignored) {
return 0;
}
}
};
回答1:
In case an exception is thrown, you return 0. That means whenever any of the arguments cannot be parsed, both are considered equal. Think of this example:
a = "01/01/2015"
b = "01/01/2016"
c = "xxx"
Then you get
comparator.compare(a,c) = 0
comparator.compare(b,c) = 0
but
comparator.compare(a,b) != 0
Solution: Try parsing each of the Strings separately, and on exception use null
like this:
private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
Date first;
try {
first = sdf.parse(o1);
} catch (Exception ignored) {
first = null;
}
Date second;
try {
second = sdf.parse(o2);
} catch (Exception ignored) {
second = null;
}
if (first == second) {
return 0;
}
if (first == null) {
return 1;
}
if (second == null) {
return -1;
}
return first.compareTo(second);
}
};
回答2:
The problem is in your try catch
block.
Even if 1 of the dates is not parsable, you are returning 0
(which means the objects are equal).
Now let's take this condition.
str1 = "invalid";
str2 = "10/10/2015"; //Consider this to be valid format.
str3 = "12/10/2015";
Now, let's run over the comparisons,
- compare str1 and str2: Returns
0
(which means equal) - compare str1 and str3: Returns
0
(which means equal)
Which means, when you compare str2 and str3, then should be equal
. (A=B
and A=C
means B=C
).
But when it compares, it returns a negative number. Hence the exception.
回答3:
Nice. You problem is actually not that hard... Imagine you have three Strings...
Date 1 = correct date string for "Today"
Date 2 = correct date string for "Tomorrow"
Date 3 = XYZ (an non-correct date String that will throw an exception when parsed)
Date 1 < Date 2, obviously.
Date 2 > Date 1, also obvious, works fine
But now the magic trick/problem:
Date 3 == Date 1 - because of your exception handling
AND
Date 3 == Date 2 - also because of that (for both you will return 0).
So there is a Date that is EQUAL to both 1 and 2, but 1 and 2 are NOT equal.
Ask yourself, where would you place Date 3 in your list? It has to be on the same "position" as Date 1 (because it is compared with == 0) AND on the same "position" as Date 2 (again, it compares with == 0 with that). BUT Date 1 and Date 2 are NOT on the same position. This is impossible, thus you are getting your "Comparison method violates its general contract!." exception.
回答4:
Your collection of Strings probably has unparsable dates. This leads to returning 0 in situations where it doesn't make sense.
String a = "badString";
String b = "20/12/2012 12:13";
String c = "20/12/2015 13:14";
b is smaller than c and c is greater than b. b and c are thus not equal. But your function says they are both equal to String a! That makes no sense and it is impossible for Collections.sort
to sort correctly.
b needs to be before c, c needs to be after b but a needs to be right next to b AND c.
A better way to handle your comparator would be to filter the List of Strings first, so that you're only comparing valid dates. Your function can then just throw a RuntimeException about still not being able to parse Dates.
回答5:
The sorting algorithm used by java.util.Arrays.sort and (indirectly) by java.util.Collections.sort has been replaced. The new sort implementation may throw an IllegalArgumentException if it detects a Comparable that violates the Comparable contract. The previous implementation silently ignored such a situation. If the previous behavior is desired, you can use the new system property, java.util.Arrays.useLegacyMergeSort, to restore previous mergesort behavior
来源:https://stackoverflow.com/questions/31556639/illegalargumentexception-on-collections-sort-method