64 votes

Est-il possible que TreeSet soit égal à HashSet mais pas que HashSet soit égal à TreeSet ?

J'ai eu un entretien aujourd'hui et la personne qui l'a passé m'a laissé perplexe en me demandant s'il était possible que TreeSet est égal à HashSet mais pas HashSet est égal à TreeSet . J'ai dit "non", mais selon lui, la réponse est "oui".

Comment est-ce possible ?

70voto

Aniket Sahrawat Points 5910

Votre interlocuteur a raison, ils n'ont pas de relation d'équivalence pour certains cas spécifiques. Il est possible que TreeSet peut être égal à HashSet et non l'inverse. Voici un exemple :

TreeSet<String> treeSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
HashSet<String> hashSet = new HashSet<>();
treeSet.addAll(List.of("A", "b"));
hashSet.addAll(List.of("A", "B"));
System.out.println(hashSet.equals(treeSet)); // false
System.out.println(treeSet.equals(hashSet)); // true

La raison en est qu'un TreeSet utilise un comparateur pour déterminer si un élément est dupliqué alors que HashSet utilise equals .

Citation : TreeSet :

Notez que l'ordre maintenu par un ensemble (qu'un comparateur explicite soit fourni ou non) doit être compatible avec les égaux s'il doit implémenter correctement l'interface Set.

19voto

kewne Points 1133

Ce n'est pas possible. sans violer le contrat d'égal à égal ou de Seth . La définition des égaux en Java nécessite une symétrie, c'est-à-dire que a.equals(b) doit être le même que b.equals(a) .

En fait, le la documentation même de Set dit

Renvoie true si l'objet spécifié est également un ensemble, si les deux ensembles ont la même taille et si chaque membre de l'ensemble spécifié est contenu dans cet ensemble (ou de manière équivalente, si chaque membre de cet ensemble est contenu dans l'ensemble spécifié). Cette définition garantit que la méthode equals fonctionne correctement dans différentes implémentations de l'interface set.

11voto

NON il est impossible de le faire sans violer le contrat général de l'entreprise. est égal à de la méthode Object qui nécessite symétrie c'est-à-dire x.equals(y) si et seulement si y.equals(x) .

MAIS , les classes TreeSet y HashSet mettre en œuvre la est égal à contrat de la Set l'interface différemment. Ce contrat exige, entre autres, que chaque membre de l'ensemble spécifié soit contenu dans cet ensemble. Pour déterminer si un élément se trouve dans l'ensemble, la fonction contains est appelée, ce qui pour Ensemble d'arbres utilise Comparator et pour HashSet utilise hashCode .

Et enfin :

OUI cela est possible dans certains cas.

2voto

Tashkhisi Points 1628

Il s'agit d'une citation du livre Java Generics and Collections :

En principe, tout ce qu'un client doit savoir, c'est comment respecter sa part du contrat. son côté du contrat ; s'il ne le fait pas, tout est fini et il ne devrait pas être nécessaire de dire exactement ce que le fournisseur fera. et il ne devrait pas être nécessaire de dire exactement ce que le fournisseur fera.

La réponse est donc : Oui, cela peut arriver, mais seulement si vous ne respectez pas votre part du contrat avec Java. Ici, vous pouvez dire que Java a violé la propriété symétrique de l'égalité, mais si cela se produit, soyez sûr que c'est vous qui avez d'abord rompu le contrat de certaines autres interfaces. Java a déjà documenté ce comportement.

En général, vous devriez lire la documentation de Comparator y Comparable pour les utiliser correctement dans les collections triées.

Cette question trouve une réponse dans Effective Java, troisième édition, point 14, pages 66-68.

Voici une citation tirée du livre lorsqu'il s'agit de définir le contrat de mise en œuvre Comparable (notez que ce n'est qu'une partie de l'ensemble du contrat) :

- Il est fortement recommandé, mais pas obligatoire, que (x.compareTo(y) == 0) == (x.equals(y)). En règle générale, toute classe qui implémente l'interface Comparable et qui viole cette condition doit clairement l'indiquer. indiquer clairement ce fait. Le langage recommandé est le suivant : "Note : This class has un ordre naturel qui est incompatible avec equals".

Il est dit Il est fortement recommandé, mais pas obligatoire cela signifie que vous êtes autorisé à avoir des cours pour lesquels x.compareTo(y)==0 ne signifie pas x.equal(y)==true (Mais s'ils sont implémentés de cette manière, vous ne pouvez pas les utiliser comme élément dans des collections triées, ce qui est exactement le cas avec les éléments suivants BigDecimal )

Le paragraphe du livre décrivant cette partie du contrat de Comparable mérite d'être mentionnée :

Il s'agit d'une suggestion forte plutôt que d'une véritable exigence. indique simplement que le test d'égalité imposé par la méthode compareTo doit généralement retourner les mêmes résultats que la méthode equals. Si cette disposition est respectée, on dit que l'ordre imposé par la méthode compareTo est dit être cohérent avec equals. Si elle est violée, on dit que l'ordre est dit être incompatible avec equals. Une classe dont la méthode compareTo impose un ordre qui n'est pas cohérent avec equals fonctionnera quand même, mais mais les collections triées qui contiennent des éléments de la classe peuvent ne pas contrat général des interfaces de collecte appropriées (Collection, Set, ou Maestro). (Collection, Set, ou Map). Ceci est dû au fait que les contrats généraux de ces interfaces sont définis en termes de méthode equals, mais les collections triées utilisent le test d'égalité. triées utilisent le test d'égalité imposé par compareTo à la place de equals. equals. Ce n'est pas une catastrophe si cela se produit, mais il faut en être conscient. mais il faut en être conscient.

En fait, nous avons quelques classes dans Java même qui n'ont pas suivi cette recommandation. BigDecimal est l'un d'entre eux et cela est mentionné dans le livre.

Par exemple, considérons la classe BigDecimal, dont la méthode compareTo est incompatible avec equals. Si vous créez une instance vide de HashSet et que vous puis ajoutez un nouveau BigDecimal("1.0") et un nouveau BigDecimal("1.00"), l'ensemble contiendra deux éléments car les deux instances BigDecimal ajoutées à l'ensemble à l'ensemble sont inégales lorsqu'elles sont comparées à l'aide de la méthode equals. Si, Cependant, si vous effectuez la même procédure en utilisant un TreeSet au lieu d'un HashSet, l'ensemble ne contiendra qu'un seul élément car les deux instances BigDecimal sont égales lorsqu'elles sont comparées à l'aide de la méthode equals. instances BigDecimal sont égales lorsqu'elles sont comparées à l'aide de la méthode compareTo pour les comparer. (Voir la documentation de BigDecimal pour plus de détails).

Cependant, ce comportement est documenté dans BigDecimal Documentation. Jetons un coup d'œil à cette partie de la documentation :

Note : il faut faire attention si des objets BigDecimal sont utilisés comme clés clés dans un SortedMap ou des éléments dans un SortedSet, car l'ordre naturel des BigDecimal n'est pas compatible avec les égaux. Voir Comparable, SortedMap ou SortedSet pour plus d'informations.

Ainsi, bien que vous puissiez écrire un code comme celui ci-dessous, vous ne devez pas le faire car le programme BigDecimal a interdit cet usage :

Set<BigDecimal> treeSet = new TreeSet<>();
Set<BigDecimal> hashSet = new HashSet<>();
treeSet.add(new BigDecimal("1.00"));
treeSet.add(new BigDecimal("2.0"));
hashSet.add(new BigDecimal("1.00"));
hashSet.add(new BigDecimal("2.00"));
System.out.println(hashSet.equals(treeSet)); // false
System.out.println(treeSet.equals(hashSet)); // true

Notez que Comparable sera utilisé comme ordre naturel des éléments si vous ne passez pas de comparateur à l'adresse suivante TreeSet o TreeMap la même chose peut arriver quand vous passez Comparator à ces constructeurs de classe. Ceci est mentionné dans le Comparator documentation :

L'ordre imposé par un comparateur c sur un ensemble d'éléments S est dit cohérent avec equals si et seulement si c.compare(e1, e2)==0 a la même valeur booléenne que e1.equals(e2) pour chaque e1 et e2 dans S. la même valeur booléenne que e1.equals(e2) pour chaque e1 et e2 dans S.

Il faut être prudent lorsqu'on utilise un comparateur capable de d'imposer un ordre incompatible avec equals pour ordonner un ensemble trié (ou une carte triée). (ou une carte triée). Supposons qu'un ensemble trié (ou une carte triée) avec un comparateur explicite comparateur explicite c est utilisé avec des éléments (ou clés) tirés d'un ensemble S. Si l'ordre imposé par c à S est incompatible avec les égaux, l'ensemble (ou la carte) trié(e) l'ensemble trié (ou la carte triée) se comportera "bizarrement". En particulier, l'ensemble trié En particulier, l'ensemble trié (ou la carte triée) violera le contrat général pour l'ensemble (ou la carte), qui est défini en termes d'égalité. carte), qui est défini en termes d'égaux.

Donc, considérant ce document de Comparator L'exemple suivant donné par @Aniket Sahrawat n'est pas supporté pour fonctionner :

TreeSet<String> treeSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
HashSet<String> hashSet = new HashSet<>();
treeSet.addAll(List.of("A", "b"));
hashSet.addAll(List.of("A", "B"));
System.out.println(hashSet.equals(treeSet)); // false
System.out.println(treeSet.equals(hashSet)); // true

En résumé, la réponse est la suivante : oui, cela peut arriver, mais seulement si vous rompez le contrat documenté de l'une des interfaces susmentionnées. SortedSet , Comparable , Comparator ).

1voto

Giorgi Tsiklauri Points 370

Il y a déjà de bonnes réponses, mais je voudrais aborder la question d'un point de vue un peu plus général.

Dans le domaine des mathématiques, de la logique et, par conséquent, de l'informatique, "est égal à" est un Relation binaire symétrique ce qui signifie que si A is equal to B puis B is equal to A .

Donc, si TreeSet X est égal à HashSet Y alors HashSet Y doit être égal à TreeSet X et cela doit être vrai. toujours .

Si, toutefois, la propriété symétrique de la Égalité est violée (c'est-à-dire que Égalité n'est pas mis en œuvre correctement), alors x.equals(y) ne signifie pas forcément y.equals(x) .


La documentation de Objet#équivalents en Java, déclare explicitement, que :

La méthode equals met en œuvre une relation d'équivalence sur les références d'objets non nuls.

par conséquent, il met en œuvre le site propriété symétrique et si ce n'est pas le cas, alors il viole l'égalité, en général, et viole la méthode Object#equals, notamment en Java.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X