web-dev-qa-db-fra.com

java.lang.IllegalArgumentException: la méthode de comparaison viole son contrat général

Salut ci-dessous est ma méthode de comparaison de mon comparateur. Je ne suis pas sûr de ce qui ne va pas. J'ai cherché d'autres questions et réponses intitulées similaires sur le dépassement de capacité de la pile, mais je ne savais pas ce qui n'allait pas avec ma méthode, mais je continue à avoir Java.lang.IllegalArgumentException: la méthode de comparaison viole son contrat général!

Toute aide sera fortement appréciée

public int compare(Node o1, Node o2)
{
    HashMap<Integer,Integer> childMap = orderMap.get(parentID);
    if(childMap != null && childMap.containsKey(o1.getID()) && 
                           childMap.containsKey(o2.getID()))
    {
        int order1 = childMap.get(o1.getID());
        int order2 = childMap.get(o2.getID());

        if(order1<order2) 
            return -1;
        else if(order1>order2) 
            return 1;
        else 
            return 0;
    }
    else
        return 0;
}

Ajout de l'exception que je reçois

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)
45
Chandu

Votre méthode compare() est pas transitive . Si A == B Et B == C, Alors A doit être égal à C.

Considérons maintenant ce cas:

Pour A, B et C, supposons que la méthode containsKey() renvoie les résultats suivants:

  • childMap.containsKey(A.getID()) renvoie true
  • childMap.containsKey(B.getID()) renvoie false
  • childMap.containsKey(C.getID()) renvoie true

En outre, considérons les commandes pour A.getId()! = B.getId().

Alors,

  1. A et B renverraient 0, Car la condition externe if sera false => A == B
  2. B et C renverraient 0, Car la condition externe if sera false => B == C

Mais, A et C pourraient renvoyer -1 Ou 1, En fonction de votre test dans le bloc if. Donc, A != C. Cela viole le principe de transitivité.

Je pense que vous devriez ajouter une condition à l'intérieur de votre bloc else, qui effectue une vérification similaire à celle du bloc if.

58
Rohit Jain

Je pense que le problème est dans votre cas par défaut. Considérez l'ensemble des nœuds A, B et C, où les ID sont 'a', 'b' Et 'c'. Pensez en outre que votre childMap, qui contient les informations de commande relatives, a le contenu suivant:

{ 'a' => 1, 'c' => 3 }

Maintenant, si vous exécutez votre méthode compare sur A et B, vous retournez 0, Indiquant que A et B sont équivalents. De plus, si vous comparez B et C, vous retournez toujours 0. Cependant, si vous comparez A et C, vous retournez -1, Indiquant que A est plus petit. Ceci viole la propriété de transitivité de le Comparator contrat :

L'implémenteur doit également s'assurer que la relation est transitive: ((compare(x, y)>0) && (compare(y, z)>0)) Implique compare(x, z)>0.

Enfin, l'implémenteur doit s'assurer que compare(x, y)==0 implique que sgn(compare(x, z))==sgn(compare(y, z)) pour tout z.

Vous ne pouvez pas traiter les "éléments pour lesquels aucun ordre n'a été attribué" comme ayant la valeur "quelque part vaguement au milieu", car les algorithmes de tri ne savent pas où les placer. Si vous souhaitez conserver cette approche, dans le cas où la valeur ne serait pas présente dans la carte, vous devez affecter une valeur fixe au numéro de référence. quelque chose comme 0 ou MIN_INT est un choix raisonnable (mais tout choix doit être documenté dans la Javadoc pour compare!).

4
chrylis