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.

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) {
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 true | exécuter equals() |
renvoie false | n'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 ... |
---|
true | true ou false |
false | false |
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 ... |
---|
true | true |
false | true 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