Java SortedSet + Comparator, cohérence avec equals () question
-
19-09-2019 - |
Question
Je voudrais avoir une SortedSet des collections (se SETS, dans ce cas, mais pas nécessairement en général), ce genre selon la taille de la collection. Cela semble violer la proscription d'avoir le Comparator être compatible avec equals () -. Par exemple, deux collections peuvent être inégales (en ayant différents éléments), mais comparer à la même valeur (parce qu'ils ont le même nombre d'éléments)
En théorie, je pourrais aussi mettre dans les moyens de comparaison pour trier des ensembles de tailles égales, mais l'utilisation de ce genre négligerait de cela, et il n'y a pas vraiment un moyen utile + intuitive pour comparer les collections de taille égale (au moins, dans mon cas particulier), de sorte que semble comme un déchet.
Est-ce que cas d'incompatibilité semble comme un problème?
La solution
ChssPly76 a écrit dans un commentaire, vous pouvez utiliser hashCode pour décider de l'appel compareTo dans le cas où deux collections ont la même taille, mais ne sont pas égaux. Cela fonctionne très bien, sauf dans les rares cas où vous avez deux collections avec la même taille, ne sont pas égaux, mais ont la même hashCode. Certes, les chances que cela se produise sont assez petits, mais il est concevable. Si vous voulez être très prudent, au lieu de hashCode, utilisez System.identityHashCode à la place. Cela devrait vous donner un numéro unique pour chaque collection, et vous ne devriez pas obtenir les collisions.
A la fin de la journée, cela vous donne la fonctionnalité d'avoir les collections dans le Set trié par taille, avec ordre arbitraire dans le cas de deux collections avec une taille correspondant. Si cela est tout ce que vous avez besoin, ce n'est pas beaucoup plus lent que la comparaison habituelle serait. Si vous avez besoin de l'ordre d'être cohérent entre les différentes instances de JVM, cela ne fonctionnera pas et vous devrez le faire d'une autre manière.
pseudocode:
if (a.equals(b)) {
return 0;
} else if (a.size() > b.size()) {
return 1;
} else if (b.size() > a.size()) {
return -1;
} else {
return System.identityHashCode(a) > System.identityHashCode(b) ? 1 : -1;
}
Autres conseils
SortedSet
interface étend Set
et doivent donc se conformer au contrat décrit dans la spécification de Set
.
La seule façon d'y parvenir est d'avoir le comportement de la méthode de equal()
de votre élément soit compatible avec votre Comparator
-. La raison en est que Set
de base fonctionne sur la base de l'égalité alors que SortedSet
fonctionne sur la base de comparaison
Par exemple, méthode add()
définie dans le noyau Set interface spécifie que vous ne pouvez pas ajouter un élément à l'ensemble s'il est déjà un élément dont la méthode equal()
retournerait vrai avec ce nouvel élément comme argument. Eh bien, SortedSet
n'utilise pas equal()
, il utilise compareTo()
. Donc, si vos déclarations de compareTo()
false
votre élément sera ajouté même si equals()
devait revenir true
, brisant ainsi le contrat de Set
.
Rien de tout cela est un problème pratique en soi, cependant. comportement SortedSet
est toujours cohérente, même si compare()
vs equals()
ne sont pas.
Cela semble violer la proscription d'avoir la cohérence soit Comparator avec equals () - à savoir, deux collections peuvent être inégaux (en ayant différents éléments), mais se comparent à la même valeur (parce qu'ils ont la même nombre d'éléments).
Il n'y a pas d'exigence, soit déclaré (dans le Javadoc) ou implicite, qu'un Comparator
soit compatible avec la mise en œuvre de boolean equals(Object)
d'un objet.
Notez que Comparable
et Comparator
sont interfaces distinctes avec des fins différentes. Comparable
est utilisé pour définir un ordre « naturel » pour une classe. Dans ce contexte, ce serait une mauvaise idée pour equals
et compateTo
incompatible. En revanche, un Comparator
est utilisé lorsque vous souhaitez utiliser un ordre différent de l'ordre naturel d'une classe.
EDIT:. Voici le paragraphe complet de la Javadoc pour SortedSet
Notez que l'ordre maintenu par une ensemble trié (ou non explicite comparateur est prévu) doit être compatible avec égaux si le tri ensemble est d'appliquer correctement l'ensemble interface. (Voir la Comparable Interface ou interface de comparateur pour une définition précise de cohérence avec égaux.) Il en est ainsi parce que la Paramétrage de l'interface est définie en termes de Egaux opération, mais Énuméré ensemble effectue toutes les comparaisons d'éléments en utilisant son compareTo (ou comparer) procédé, de sorte que deux éléments qui sont jugée égale par cette méthode sont, de Du point de vue de l'ensemble trié, égal. Le comportement d'un ensemble est trié bien définie, même si sa commande est incompatible avec égaux; c'est juste ne parvient pas à obéir au contrat général de l'ensemble d'interface.
Je l'ai mis en évidence la dernière phrase. Le point est qu'un tel SortedSet fonctionnera comme vous le plus attendre probable, mais le comportement de certaines opérations ne correspond pas exactement aux spécifications de Set
... car la spécification définit leur comportement en termes de méthode equals
.
Donc, en fait, est une exigence de cohérence déclaré (mon erreur), mais les conséquences de l'ignorer ne sont pas aussi mauvais que vous pourriez penser. Bien sûr, il appartient de décider si vous devez le faire. À mon avis, il devrait être OK, à condition que vous commentez le code à fond et assurez-vous que le SortedSet ne « fuite ».
Cependant, il est clair pour moi que pour les collections qui Comparator ne porte que sur une collection « taille » va travailler ... à partir d'un point de vue sémantique. Je veux dire, est-ce que vous voulez vraiment dire que toutes les collections avec (par exemple) 2 éléments sont égaux? Cela signifie que votre jeu ne peut jamais contenir une collection d'une taille donnée ...
Il n'y a aucune raison pour laquelle un Comparator
doit retourner les mêmes résultats que equals()
. En fait, l'API Comparator
a été introduite parce equals()
ne suffit pas: Si vous voulez trier une collection, vous devez savoir si deux éléments sont plus ou moins grande
Il est un peu étrange que SortedSet comme une partie de l'API standard rompt le contrat défini dans l'ensemble d'interface et utilise le Comparator pour définir l'égalité au lieu de la méthode equals, mais comment cela est.
Si votre problème réel est de trier une collection de collections en fonction de la taille des collections containted, vous êtes mieux avec une liste que vous pouvez trier en utilisant Collections.sort (Liste COMPARATEUR>);