Java Challengers # 4: Comparaison d'objets avec equals () et hashCode ()

Java Challengers # 4: Comparaison d'objets avec equals () et hashCode ()


En prĂ©vision du lancement d'un nouveau fil de discussion sur le cours "DĂ©veloppeur Java", nous continuons Ă  traduire une sĂ©rie d'articles Java Challengers, dont les parties prĂ©cĂ©dentes peuvent ĂȘtre lues sur les liens ci-dessous:



C'est parti!


Dans cet article, vous apprendrez comment les méthodes equals() et hashCode() sont liées et comment elles sont utilisées lors de la comparaison d'objets.


equals-hashcode


Sans utiliser equals() et hashCode() pour comparer l'état de deux objets, nous devons écrire beaucoup de comparaisons " if " comparant chaque champ de l'objet. Cette approche rend le code déroutant et difficile à lire. En travaillant ensemble, ces deux méthodes permettent de créer un code plus flexible et cohérent.


Le code source de l'article est ici .


Remplacer equals () et hashCode ()


La substitution de mĂ©thode est une technique par laquelle le comportement d'une classe ou d'une interface parente est réécrit (redĂ©fini) dans une sous-classe (voir Java Challengers # 3: Polymorphism and Inheritance , Eng. ). En Java, chaque objet a des mĂ©thodes equals() et hashCode() , et pour fonctionner correctement, elles doivent ĂȘtre remplacĂ©es.


Pour comprendre comment fonctionne la redéfinition de equals() et hashCode() , examinons leur implémentation dans les classes de base Java. Voici la méthode equals() de la classe Object . La méthode vérifie si l'instance actuelle correspond à l'objet obj transmis.


 public boolean equals(Object obj) { return (this == obj); } 

Examinons maintenant la hashCode() dans la classe Object .


 @HotSpotIntrinsicCandidate public native int hashCode(); 

Ceci est natif - une méthode qui est écrite dans un autre langage, tel que C, et elle renvoie un code numérique associé à l'adresse mémoire de l'objet. (Si vous n'écrivez pas de code JDK, il n'est pas important de savoir exactement comment cette méthode fonctionne.)
Note du traducteur: la valeur associée à l'adresse n'est pas complÚtement correcte ( merci à vladimir_dolzhenko ). La machine virtuelle Java HotSpot utilise par défaut des nombres pseudo aléatoires. La description de l'implémentation de hashCode () pour HotSpot est ici et ici .


Si les mĂ©thodes equals() et hashCode() ne sont pas remplacĂ©es, les mĂ©thodes de la classe Object dĂ©crites ci-dessus seront appelĂ©es Ă  la place. Dans ce cas, les mĂ©thodes ne remplissent pas le vĂ©ritable objectif de equals() et hashCode() , qui est de vĂ©rifier si les objets ont le mĂȘme Ă©tat.


En rÚgle générale, remplacer equals() remplace également hashCode() .


Comparaison d'objets avec equals ()


La méthode equals() est utilisée pour comparer des objets. Pour déterminer si les objets sont identiques ou non, equals() compare les valeurs des champs d'objet:


 public class EqualsAndHashCodeExample { public static void main(String... args){ System.out.println(new Simpson("Homer", 35, 120) .equals(new Simpson("Homer",35,120))); System.out.println(new Simpson("Bart", 10, 120) .equals(new Simpson("El Barto", 10, 45))); System.out.println(new Simpson("Lisa", 54, 60) .equals(new Object())); } static class Simpson { private String name; private int age; private int weight; public Simpson(String name, int age, int weight) { this.name = name; this.age = age; this.weight = weight; } @Override public boolean equals(Object o) { // 1 if (this == o) { return true; } // 2 if (o == null || getClass() != o.getClass()) { return false; } // 3 Simpson simpson = (Simpson) o; return age == simpson.age && weight == simpson.weight && name.equals(simpson.name); } } } 

Regardons la mĂ©thode equals() . La premiĂšre comparaison compare l'instance actuelle de this avec l' o passĂ©. S'il s'agit du mĂȘme objet, equals() renverra true .


La deuxiÚme comparaison vérifie si l'objet passé est null et de quel type il s'agit. Si l'objet transféré est d'un type différent, alors les objets ne sont pas égaux.


Enfin, equals() compare les champs d'objets. Si deux objets ont les mĂȘmes valeurs de champ, alors les objets sont les mĂȘmes.


Analyse des options de comparaison d'objets


Examinons maintenant les options de comparaison des objets dans la méthode main() . Tout d'abord, nous comparons deux objets Simpson :


 System.out.println( new Simpson("Homer", 35, 120).equals( new Simpson("Homer", 35, 120))); 

Les champs de ces objets ont les mĂȘmes valeurs, donc le rĂ©sultat sera true .


Comparez encore les deux objets Simpson :


 System.out.println( new Simpson("Bart", 10, 45).equals( new Simpson("El Barto", 10, 45))); 

Les objets ici sont similaires, mais la signification des noms est différente: Bart et El Barto . Par conséquent, le résultat sera false .


Enfin, comparons l'objet Simpson et l'instance de la classe Object :


 System.out.println( new Simpson("Lisa", 54, 60).equals( new Object())); 

Dans ce cas, le résultat sera false , car les types d'objets sont différents.


est égal à () contre ==


À premiĂšre vue, il semble que l'opĂ©rateur == et la mĂ©thode equals() font la mĂȘme chose, mais, en fait, ils fonctionnent diffĂ©remment. L'opĂ©rateur == compare si deux liens pointent vers le mĂȘme objet. Par exemple:


 Simpson homer = new Simpson("Homer", 35, 120); Simpson homer2 = new Simpson("Homer", 35, 120); System.out.println(homer == homer2); 

Nous avons créé deux instances différentes de Simpson utilisant le new opérateur. Par conséquent, les variables homer et homer2 pointeront vers des objets différents dans le tas . Ainsi, en conséquence, nous obtenons false .


Dans l'exemple suivant, nous utilisons la méthode equals() substituée:


 System.out.println(homer.equals(homer2)); 

Dans ce cas, les champs seront comparĂ©s. Étant donnĂ© que les deux objets Simpson ont les mĂȘmes valeurs de champ, le rĂ©sultat sera true .


Identification des objets avec hashCode ()


La hashCode() est utilisée pour optimiser les performances lors de la comparaison d'objets. La hashCode() renvoie un identifiant unique pour chaque objet, ce qui simplifie la comparaison des états des objets.


Si le code de hachage d'un objet ne correspond pas au code de hachage d'un autre objet, vous pouvez omettre la mĂ©thode equals() : vous savez simplement que les deux objets ne correspondent pas. D'un autre cĂŽtĂ©, si le code de hachage est le mĂȘme, vous devez exĂ©cuter la mĂ©thode equals() pour dĂ©terminer si les valeurs de champ correspondent.


Prenons un exemple pratique avec hashCode() .


 public class HashcodeConcept { public static void main(String... args) { Simpson homer = new Simpson(1, "Homer"); Simpson bart = new Simpson(2, "Homer"); boolean isHashcodeEquals = homer.hashCode() == bart.hashCode(); if (isHashcodeEquals) { System.out.println("   equals."); } else { System.out.println("    equals, .. " + " ,  ,     ."); } } static class Simpson { int id; String name; public Simpson(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Simpson simpson = (Simpson) o; return id == simpson.id && name.equals(simpson.name); } @Override public int hashCode() { return id; } } } 

La hashCode() , qui renvoie toujours la mĂȘme valeur, est valide mais pas efficace. Dans ce cas, la comparaison retournera toujours true , donc la mĂ©thode equals() sera toujours exĂ©cutĂ©e. Dans ce cas, il n'y a pas d'amĂ©lioration des performances.


Utilisation de equals () et hashCode () avec des collections


Les classes qui implĂ©mentent l'interface Set (set) doivent empĂȘcher l'ajout d'Ă©lĂ©ments en double. Voici quelques classes qui implĂ©mentent l'interface Set :



Seuls des Ă©lĂ©ments uniques peuvent ĂȘtre ajoutĂ©s Ă  Set . Ainsi, si vous souhaitez ajouter un Ă©lĂ©ment, par exemple, Ă  un HashSet , vous devez d'abord utiliser les mĂ©thodes equals() et hashCode() pour vous assurer que cet Ă©lĂ©ment est unique. Si les mĂ©thodes equals() et hashCode() n'ont pas Ă©tĂ© remplacĂ©es, vous risquez d'insĂ©rer des valeurs en double.


Regardons la partie implémentation de la méthode add() dans un HashSet :


 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; 

Avant d'ajouter un nouvel élément, le HashSet vérifie si l'élément existe dans cette collection. Si l'objet correspond, le nouvel élément ne sera pas inséré.


Les mĂ©thodes equals() et hashCode() ne sont pas utilisĂ©es uniquement dans Set . Ces mĂ©thodes sont Ă©galement requises pour HashMap , Hashtable et LinkedHashMap . En rĂšgle gĂ©nĂ©rale, si vous voyez une collection avec le prĂ©fixe "Hash" , vous pouvez ĂȘtre sĂ»r que pour son fonctionnement correct, une substitution des mĂ©thodes hashCode() et equals() est requise.


Recommandations pour l'utilisation de equals () et hashCode ()


ExĂ©cutez la mĂ©thode equals() uniquement pour les objets avec le mĂȘme code de hachage. N'exĂ©cutez pas equals() si le code de hachage est diffĂ©rent.


Tableau 1. Comparaison des codes de hachage


Si comparaison avec hashCode () ...Ça ...
renvoie trueexécuter equals()
renvoie falsen'exécute pas equals()

Ce principe est principalement utilisé dans les collections Set ou Hash pour des raisons de performances.


RĂšgles de comparaison d'objets


Lorsque la comparaison hashCode() renvoie false , la méthode equals() doit également renvoyer false . Si le code de hachage est différent, alors les objets ne sont certainement pas égaux.


Tableau 2: Comparaison d'objets avec hashCode ()


Lorsque la comparaison hashCode() revient ...La méthode equals() devrait retourner ...
truetrue ou false
falsefalse

Lorsque la mĂ©thode equals() renvoie true , cela signifie que les objets sont Ă©gaux dans toutes les valeurs et tous les attributs . Dans ce cas, la comparaison du code de hachage doit Ă©galement ĂȘtre vraie.


Tableau 3. Comparaison d'objets avec des égaux ()


Lorsque la méthode equals() retourne ...La hashCode() devrait retourner ...
truetrue
falsetrue ou false

Résoudre le problÚme sur equals () et hashCode ()


Il est temps de tester vos connaissances sur les méthodes equals() et hashCode() . La tùche consiste à découvrir le résultat de plusieurs equals() et la taille totale de la collection Set .


Pour commencer, étudiez attentivement le code suivant:


 public class EqualsHashCodeChallenge { public static void main(String... args) { System.out.println(new Simpson("Bart").equals(new Simpson("Bart"))); Simpson overriddenHomer = new Simpson("Homer") { public int hashCode() { return (43 + 777) + 1; } }; System.out.println(new Simpson("Homer").equals(overriddenHomer)); Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge"))); set.add(new Simpson("Homer")); set.add(overriddenHomer); System.out.println(set.size()); } static class Simpson { String name; Simpson(String name) { this.name = name; } @Override public boolean equals(Object obj) { Simpson otherSimpson = (Simpson) obj; return this.name.equals(otherSimpson.name) && this.hashCode() == otherSimpson.hashCode(); } @Override public int hashCode() { return (43 + 777); } } } 

Tout d'abord, analysez le code, réfléchissez au résultat. Et alors seulement, exécutez le code. L'objectif est d'améliorer vos compétences en analyse de code et d'apprendre les concepts de base de Java afin d'améliorer votre code.


Quel sera le résultat?.


 A) true true 4 B) true false 3 C) true false 2 D) false true 3 

Qu'est-il arrivé? Comprendre equals () et hashCode ()


Dans la premiĂšre comparaison, le rĂ©sultat de equals() est true , car les Ă©tats des objets sont les mĂȘmes et la hashCode() renvoie la mĂȘme valeur pour les deux objets.


Dans la deuxiÚme comparaison, la hashCode() été remplacée pour la variable hashCode() . Pour les deux objets Simpson , le nom est "Homer" , mais pour overriddenHomer la hashCode() renvoie une valeur différente. Dans ce cas, le résultat de la méthode equals() sera false , car il contient une comparaison avec le code de hachage.


Vous devez avoir réalisé qu'il y aura trois objets Simpson dans la collection. Faisons-le.


Le premier objet de l'ensemble sera inséré comme d'habitude:


 new Simpson("Homer"); //  

L'objet suivant sera également inséré de la maniÚre habituelle, car il contient une valeur différente de l'objet précédent:


 new Simpson("Marge"); //  

Enfin, l'objet Simpson suivant a la mĂȘme valeur de nom que le premier objet. Dans ce cas, l'objet ne sera pas insĂ©rĂ©:


 set.add(new Simpson("Homer")); //   

Comme nous le savons, l'objet overridenHomer utilise une valeur de hachage différente, contrairement à une instance Simpson("Homer") normale Simpson("Homer") . Pour cette raison, cet article sera inséré dans la collection:


 set.add(overriddenHomer); //  

La réponse


La bonne réponse est B. La conclusion sera:


 true false 3 

Erreurs courantes avec equals () et hashCode ()


  • Absence de substitution de hashCode() avec substitution de equals() ou vice versa.
  • Manque de remplacer equals() et hashCode() lors de l'utilisation de collections de hachage comme HashSet .
  • Renvoyer une valeur constante dans la hashCode() au lieu de renvoyer un code unique pour chaque objet.
  • Utilisation Ă©quivalente de == et equals() . L'opĂ©rateur == compare les rĂ©fĂ©rences d'objets, tandis que la mĂ©thode equals() compare les valeurs des objets.

Ce que vous devez retenir sur equals () et hashCode ()


  • Il est recommandĂ© de toujours remplacer les mĂ©thodes equals() et hashCode() dans vos POJO ( russe , anglais )
  • Utilisez un algorithme efficace pour crĂ©er un code de hachage unique.
  • Lors de la substitution de la hashCode() equals() , remplacez toujours la hashCode() .
  • La mĂ©thode equals() doit comparer l'Ă©tat complet des objets (valeurs des champs).
  • La hashCode() peut ĂȘtre un identifiant POJO (ID).
  • Si le rĂ©sultat de la comparaison du code de hachage des deux objets est false , la mĂ©thode equals() doit Ă©galement ĂȘtre false .
  • Si equals() et hashCode() ne hashCode() pas redĂ©finis lors de l'utilisation de collections de hachage, alors la collection aura des Ă©lĂ©ments en double.

En savoir plus sur Java



Traditionnellement, j'attends vos commentaires et vous invite à une leçon ouverte , qui sera animée par notre professeur Sergei Petrelevich le 18 mars

Source: https://habr.com/ru/post/fr443710/


All Articles