Pourquoi et comment redéfinir la méthode hashCode?
sobes.tech IA
Réponse de l'IA
La méthode hashCode est utilisée pour obtenir le code de hachage entier d'un objet. Elle est nécessaire pour le bon fonctionnement des collections basées sur des tables de hachage, telles que HashMap, HashSet et HashTable. Lorsqu'un objet est ajouté à une telle collection, son code de hachage est utilisé pour déterminer la "caisse" (bucket) où l'objet sera placé. Lors de la recherche d'un objet, le code de hachage est recalculé pour identifier rapidement la caisse à examiner.
Il est nécessaire de redéfinir hashCode si vous redéfinissez la méthode equals. Selon le contrat entre equals et hashCode:
- Si deux objets sont égaux selon
equals, leurs codes de hachage, retournés parhashCode, doivent être identiques. - Si deux objets ne sont pas égaux selon
equals, leurs codes de hachage ne sont pas obligés d'être différents, mais pour de meilleures performances des collections, il est préférable qu'ils le soient.
Si vous redéfinissez equals sans redéfinir hashCode, les objets égaux auront des codes de hachage différents (sauf coincidences aléatoires), ce qui entraînera un fonctionnement incorrect des collections:
- Les objets considérés comme égaux par
equalsseront placés dans différentes caisses. - La recherche d'un objet égal peut échouer, car seule une caisse déterminée par le code de hachage de l'objet recherché sera vérifiée.
Pour redéfinir hashCode, on combine généralement le code de hachage des champs de l'objet qui participent à la comparaison d'égalité dans equals.
Les méthodes les plus courantes pour redéfinir:
-
Utilisation de
Objects.hash()(depuis Java 7): La méthode la plus simple et recommandée. Elle est sûre pournullet utilise les codes de hachage des champs fournis.// Exemple d'utilisation de Objects.hash() import java.util.Objects; public class MyClass { private int field1; private String field2; // ... constructeur, getters, setters @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyClass myClass = (MyClass) o; return field1 == myClass.field1 && Objects.equals(field2, myClass.field2); } @Override public int hashCode() { return Objects.hash(field1, field2); // On passe les champs qui participent à equals } } -
Manuellement (en utilisant un algorithme simple): Approche de bas niveau, utile pour comprendre, mais plus sujette à erreur. On utilise généralement la multiplication et l'addition simples.
// Exemple de redéfinition manuelle de hashCode public class MyClass { private int field1; private String field2; // ... constructeur, getters, setters @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyClass myClass = (MyClass) o; return field1 == myClass.field1 && Objects.equals(field2, myClass.field2); } @Override public int hashCode() { int result = 17; // Valeur initiale (tout nombre impair) result = 31 * result + field1; // On multiplie par un nombre premier impair (31 est standard) et on ajoute le hash du champ result = 31 * result + (field2 != null ? field2.hashCode() : 0); // Gestion de null pour les objets return result; } }
Le nombre premier impair standard (31) est utilisé car la multiplication par 31 peut être optimisée par le compilateur (31 * i == (i << 5) - i).
- Utilisation d'IDE: La plupart des IDE modernes (IntelliJ IDEA, Eclipse) ont des fonctions pour générer automatiquement les méthodes
equals()ethashCode(). C'est l'approche la plus sûre et recommandée, car elles génèrent du code selon les meilleures pratiques.
L'objectif principal de redéfinir hashCode est de garantir que les objets égaux ont des codes de hachage identiques, ce qui est crucial pour le bon fonctionnement des collections basées sur le hachage.