Relation entre hashCode et la méthode equals en Java

J’ai lu dans de nombreux endroits en disant que lorsque la substitution était equals méthode en Java, il fallait aussi remplacer la méthode hashCode , sinon cela violait le contrat.

Mais jusqu’à présent, je n’ai rencontré aucun problème si je ne remplace que la méthode des égaux, mais pas la méthode hashCode.

Quel est le contrat? Et pourquoi ne suis-je pas confronté à un problème lorsque je viole le contrat? Dans quel cas vais-je rencontrer un problème si je n’ai pas remplacé la méthode hashCode?

Le problème que vous rencontrerez concerne les collections où l’unicité des éléments est calculée à la fois à l’ .equals() et de .hashCode() , par exemple les clés d’un HashMap .

Comme son nom l’indique, il s’appuie sur des tables de hachage, et les compartiments de hachage sont une fonction du .hashCode() de l’object.

Si vous avez deux objects qui sont .equals() , mais ont des codes de hachage différents, vous perdez!

La partie du contrat ici qui est importante est la suivante: les objects qui sont .equals() DOIVENT avoir le même .hashCode() .

Ceci est tout documenté dans le javadoc pour Object . Et Joshua Bloch dit que vous devez le faire dans Java efficace . Assez dit.

Selon la doc, l’implémentation par défaut de hashCode renverra un nombre entier différent pour chaque object

Dans la mesure du possible, la méthode hashCode définie par la classe Object renvoie des entiers distincts pour des objects distincts. (Ceci est généralement implémenté en convertissant l’adresse interne de l’object en un entier, mais cette implémentation
la technique n’est pas requirejse par le langage de programmation JavaTM.)

Cependant, quelque temps, vous souhaitez que le code de hachage soit le même pour différents objects ayant la même signification. Par exemple

 Student s1 = new Student("John", 18); Student s2 = new Student("John", 18); s1.hashCode() != s2.hashCode(); // With the default implementation of hashCode 

Ce type de problème se produira si vous utilisez une structure de données de hachage dans l’infrastructure de collecte telle que HashTable, HashSet. En particulier avec la collection telle que HashSet, vous allez avoir un élément en double et violer le contrat Set.

Oui, il devrait être remplacé. Si vous pensez que vous devez remplacer equals() , vous devez remplacer hashCode() et vice versa. Le contrat général de hashCode () est:

  1. Chaque fois qu’il est appelé sur le même object plus d’une fois lors de l’exécution d’une application Java, la méthode hashCode doit systématiquement renvoyer le même entier, à condition qu’aucune information utilisée dans les comparaisons d’égal à égal sur l’object ne soit modifiée. Cet entier ne doit pas nécessairement restr cohérent d’une exécution d’une application à une autre exécution de la même application.

  2. Si deux objects sont égaux selon la méthode equals (Object), alors l’appel de la méthode hashCode sur chacun des deux objects doit produire le même résultat entier.

  3. Il n’est pas obligatoire que si deux objects sont inégaux selon la méthode d’égal à égal (java.lang.Object), alors l’appel de la méthode hashCode sur chacun des deux objects doit produire des résultats entiers distincts. Cependant, le programmeur doit savoir que produire des résultats entiers distincts pour des objects inégaux peut améliorer les performances des hashtables.

Voir JavaDoc de java.lang.Object

Dans hashCode() il est écrit:

Si deux objects sont égaux selon la méthode equals(Object) , alors l’appel de la méthode hashCode sur chacun des deux objects doit produire le même résultat entier .

(Accent mis par moi).

Si vous ne remplacez que equals() et non hashCode() votre classe viole ce contrat.

Ceci est également dit dans le JavaDoc de la méthode equals() :

Notez qu’il est généralement nécessaire de remplacer la méthode hashCode chaque fois que cette méthode est remplacée, afin de conserver le contrat général pour la méthode hashCode , qui stipule que les objects égaux doivent avoir des codes de hachage égaux.

Un contrat est le suivant: si deux objects sont égaux, ils doivent avoir le même code de hachage et si deux objects ne sont pas égaux, ils peuvent ou non avoir le même code de hachage.

Essayez d’utiliser votre object comme clé dans HashMap (édité après le commentaire de joachim-sauer), et vous allez commencer à rencontrer des problèmes. Un contrat est une directive, pas quelque chose qui vous est imposé.

Le contrat est que si obj1.equals(obj2) alors obj1.hashCode() == obj2.hashCode() , c’est principalement pour des raisons de performances, car les cartes utilisent principalement la méthode hashCode pour comparer les clés d’entrée.

Jetez un coup d’oeil aux Hashtables , aux Hashmaps , aux HashSets et ainsi de suite. Ils stockent tous la clé hachée comme clé. Lors de l’appel de get(Object key) le hachage du paramètre est généré et recherché dans les hachages donnés.

Si vous n’écrasez pas hashCode() et que l’instance de la clé a été modifiée (par exemple, une simple chaîne n’ayant aucune importance), le hashCode() peut générer deux codes de hachage différents pour le même object, ce qui ne permet pas de trouver votre code. clé donnée dans map.get() .