Błąd Java: metoda porównywania narusza ogólną umowę
Widziałem wiele pytań na ten temat i próbowałem rozwiązać problem, ale po godzinie googlowania i wielu próbach i błędach nadal nie mogę go naprawić. Mam nadzieję, że niektórzy z was złapią problem.
Oto co dostaję:
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.ComparableTimSort.mergeHi(ComparableTimSort.java:835)
at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:453)
at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:392)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:191)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at java.util.Collections.sort(Collections.java:155)
...
A to jest mój komparator:
@Override
public int compareTo(Object o) {
if(this == o){
return 0;
}
CollectionItem item = (CollectionItem) o;
Card card1 = CardCache.getInstance().getCard(cardId);
Card card2 = CardCache.getInstance().getCard(item.getCardId());
if (card1.getSet() < card2.getSet()) {
return -1;
} else {
if (card1.getSet() == card2.getSet()) {
if (card1.getRarity() < card2.getRarity()) {
return 1;
} else {
if (card1.getId() == card2.getId()) {
if (cardType > item.getCardType()) {
return 1;
} else {
if (cardType == item.getCardType()) {
return 0;
}
return -1;
}
}
return -1;
}
}
return 1;
}
}
Jakiś pomysł? 7 answers
Wiadomość o wyjątku jest właściwie dość opisowa. Kontrakt, o którym mówi, to przechodniość: Jeśli A > B
i B > C
to dla dowolnego A
, B
oraz C
: A > C
. Sprawdziłem to papierem i ołówkiem i twój kod wydaje się mieć kilka dziur:
if (card1.getRarity() < card2.getRarity()) {
return 1;
Nie zwracasz -1
Jeśli card1.getRarity() > card2.getRarity()
.
if (card1.getId() == card2.getId()) {
//...
}
return -1;
Zwracasz -1
jeśli identyfikatory nie są równe. Należy zwrócić -1
lub 1
w zależności od tego, który identyfikator był większy.
Spójrz na to. Poza byciem o wiele bardziej czytelny, myślę, że faktycznie powinien działać:
if (card1.getSet() > card2.getSet()) {
return 1;
}
if (card1.getSet() < card2.getSet()) {
return -1;
};
if (card1.getRarity() < card2.getRarity()) {
return 1;
}
if (card1.getRarity() > card2.getRarity()) {
return -1;
}
if (card1.getId() > card2.getId()) {
return 1;
}
if (card1.getId() < card2.getId()) {
return -1;
}
return cardType - item.getCardType(); //watch out for overflow!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-11 21:49:33
Ma też coś wspólnego z wersją JDK. Jeśli dobrze się sprawdzi w JDK6, może będzie miał opisany przez Ciebie problem w JDK 7, ponieważ metoda implementacji w jdk 7 została zmieniona.
Zobacz też:]}Opis: algorytm sortowania używany przez java.util.Arrays.sort
i (pośrednio) przez java.util.Collections.sort
został zastąpiony. Nowa implementacja sortowania może rzucić IllegalArgumentException
, Jeśli wykryje Comparable
, które naruszają kontrakt Comparable
. Poprzednia implementacja po cichu ignorowała takie sytuacja. Jeśli poprzednie zachowanie jest pożądane, można użyć nowej właściwości systemowej, java.util.Arrays.useLegacyMergeSort
, aby przywrócić poprzednie zachowanie mergesort.
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-18 13:58:54
Możesz użyć poniższej klasy, aby wskazać błędy przechodniości w swoich Komparatorach:
/**
* @author Gili Tzabari
*/
public final class Comparators
{
/**
* Verify that a comparator is transitive.
*
* @param <T> the type being compared
* @param comparator the comparator to test
* @param elements the elements to test against
* @throws AssertionError if the comparator is not transitive
*/
public static <T> void verifyTransitivity(Comparator<T> comparator, Collection<T> elements)
{
for (T first: elements)
{
for (T second: elements)
{
int result1 = comparator.compare(first, second);
int result2 = comparator.compare(second, first);
if (result1 != -result2)
{
// Uncomment the following line to step through the failed case
//comparator.compare(first, second);
throw new AssertionError("compare(" + first + ", " + second + ") == " + result1 +
" but swapping the parameters returns " + result2);
}
}
}
for (T first: elements)
{
for (T second: elements)
{
int firstGreaterThanSecond = comparator.compare(first, second);
if (firstGreaterThanSecond <= 0)
continue;
for (T third: elements)
{
int secondGreaterThanThird = comparator.compare(second, third);
if (secondGreaterThanThird <= 0)
continue;
int firstGreaterThanThird = comparator.compare(first, third);
if (firstGreaterThanThird <= 0)
{
// Uncomment the following line to step through the failed case
//comparator.compare(first, third);
throw new AssertionError("compare(" + first + ", " + second + ") > 0, " +
"compare(" + second + ", " + third + ") > 0, but compare(" + first + ", " + third + ") == " +
firstGreaterThanThird);
}
}
}
}
}
/**
* Prevent construction.
*/
private Comparators()
{
}
}
Po prostu wywołaj Comparators.verifyTransitivity(myComparator, myCollection)
przed kodem, który się nie powiedzie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-15 17:16:21
Rozważ następujący przypadek:
Pierwszy, o1.compareTo(o2)
jest nazywany. card1.getSet() == card2.getSet()
tak się składa, że to prawda i tak jest card1.getRarity() < card2.getRarity()
, więc zwracasz 1.
Następnie, o2.compareTo(o1)
jest nazywany. Znowu, {[1] } to prawda. Następnie przeskakujesz do następującej else
, wtedy card1.getId() == card2.getId()
jest prawdą, podobnie jak cardType > item.getCardType()
. Zwracasz 1 ponownie.
Z tego, o1 > o2
i o2 > o1
. Złamałeś kontrakt.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-11 21:35:58
if (card1.getRarity() < card2.getRarity()) {
return 1;
Jeśli jednak card2.getRarity()
jest mniejsza niż card1.getRarity()
możesz nie wrócić -1.
public int compareTo(Object o) {
if(this == o){
return 0;
}
CollectionItem item = (CollectionItem) o;
Card card1 = CardCache.getInstance().getCard(cardId);
Card card2 = CardCache.getInstance().getCard(item.getCardId());
int comp=card1.getSet() - card2.getSet();
if (comp!=0){
return comp;
}
comp=card1.getRarity() - card2.getRarity();
if (comp!=0){
return comp;
}
comp=card1.getSet() - card2.getSet();
if (comp!=0){
return comp;
}
comp=card1.getId() - card2.getId();
if (comp!=0){
return comp;
}
comp=card1.getCardType() - card2.getCardType();
return comp;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-31 08:23:46
Musiałem sortować według kilku kryteriów (Data i, jeśli ta sama data; inne rzeczy...). To, co działało na Eclipse ze starszą wersją Javy, nie działało już na Androidzie : metoda porównywania narusza kontrakt ...
Po przeczytaniu na StackOverflow napisałem oddzielną funkcję, którą wywołałem z compare (), Jeśli daty są takie same. Funkcja oblicza priorytet zgodnie z kryteriami i zwraca -1, 0 LUB 1 do compare (). Wygląda na to, że teraz działa.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-08 07:26:47
Dostałem ten sam błąd z klasą jak poniżej StockPickBean
. Wywołane z tego kodu:
List<StockPickBean> beansListcatMap.getValue();
beansList.sort(StockPickBean.Comparators.VALUE);
public class StockPickBean implements Comparable<StockPickBean> {
private double value;
public double getValue() { return value; }
public void setValue(double value) { this.value = value; }
@Override
public int compareTo(StockPickBean view) {
return Comparators.VALUE.compare(this,view); //return
Comparators.SYMBOL.compare(this,view);
}
public static class Comparators {
public static Comparator<StockPickBean> VALUE = (val1, val2) ->
(int)
(val1.value - val2.value);
}
}
Po otrzymaniu tego samego błędu:
Java.lang.Nielegalargumentexception: Metoda porównawcza narusza jej ogólną umowę!
Zmieniłem ten wiersz:
public static Comparator<StockPickBean> VALUE = (val1, val2) -> (int)
(val1.value - val2.value);
Do:
public static Comparator<StockPickBean> VALUE = (StockPickBean spb1,
StockPickBean spb2) -> Double.compare(spb2.value,spb1.value);
To naprawia błąd.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-09 02:09:15