JavaRush /Blog Java /Random-FR /Pause café #168. Pourquoi remplacer les méthodes égales e...

Pause café #168. Pourquoi remplacer les méthodes égales et hashcode en Java ?

Publié dans le groupe Random-FR

Pourquoi remplacer les méthodes égales et hashcode en Java ?

Source : Medium Cet article se concentre sur deux méthodes étroitement liées : equals() et hashcode() . Vous apprendrez comment ils interagissent les uns avec les autres et comment les remplacer correctement. Pause café #168.  Pourquoi remplacer les méthodes égales et hashcode en Java ?  - 1

Pourquoi remplaçons-nous la méthode equals() ?

En Java, nous ne pouvons pas surcharger le comportement des opérateurs comme == , += , -+ . Ils travaillent selon un processus donné. Par exemple, considérons le fonctionnement de l' opérateur == .

Comment fonctionne l'opérateur == ?

Il vérifie si les deux références comparées pointent vers la même instance en mémoire. L' opérateur == ne sera évalué à vrai que si les deux références représentent la même instance en mémoire. Jetons un coup d'œil à l'exemple de code :
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
      }
Disons que dans votre programme vous avez créé deux objets Person à des endroits différents et que vous souhaitez les comparer.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
D’un point de vue commercial, les deux se ressemblent, n’est-ce pas ? Mais pour la JVM, ce ne sont pas les mêmes. Puisqu’elles sont toutes deux créées à l’aide du mot-clé new , ces instances sont situées dans des segments de mémoire différents. Par conséquent, l' opérateur == renverra false . Mais si nous ne pouvons pas remplacer l' opérateur == , comment pouvons-nous dire à la JVM que nous voulons que ces deux objets soient traités de la même manière ? C'est là qu'intervient la méthode .equals() . Vous pouvez remplacer equals() pour vérifier si certains objets ont les mêmes valeurs pour certains champs afin de les considérer égaux. Vous pouvez choisir les champs à comparer. Si nous disons que deux objets Person seront identiques seulement s'ils ont le même âge et le même nom, alors l'EDI générera quelque chose comme ceci pour créer automatiquement equals() :
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
Revenons à notre exemple précédent.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
Oui, nous ne pouvons pas surcharger l' opérateur == pour comparer les objets comme nous le souhaitons, mais Java nous offre un autre moyen : la méthode equals() , que nous pouvons remplacer à notre guise. Gardez à l'esprit que si nous ne fournissons pas notre version personnalisée de .equals() (également appelée remplacement) dans notre classe, alors le .equals() prédéfini de la classe Object et l' opérateur == se comporteront de la même manière. La méthode par défaut equals() , héritée de Object , vérifiera si les deux instances comparées sont identiques en mémoire !

Pourquoi remplaçons-nous la méthode hashCode() ?

Certaines structures de données en Java, telles que HashSet et HashMap , stockent leurs éléments en fonction d'une fonction de hachage appliquée à ces éléments. La fonction de hachage est hashCode() . Si nous avons le choix de remplacer la méthode .equals() , alors nous devrions également avoir le choix de remplacer la méthode hashCode() . Il y a une raison à cela. Après tout, l'implémentation par défaut de hashCode() , héritée de Object , considère tous les objets en mémoire comme uniques ! Mais revenons à ces structures de données de hachage. Il existe une règle pour ces structures de données. Un HashSet ne peut pas contenir de valeurs en double et un HashMap ne peut pas contenir de clés en double. Un HashSet est implémenté à l'aide d'un HashMap de telle sorte que chaque valeur de HashSet soit stockée sous forme de clé dans le HashMap . Comment fonctionne HashMap ? HashMap est un tableau natif avec plusieurs segments. Chaque segment a une liste chaînée ( linkedList ). Cette liste chaînée stocke nos clés. HashMap trouve la liste liée correcte pour chaque clé à l'aide de la méthode hashCode() , puis parcourt tous les éléments de cette liste liée et applique la méthode equals() à chacun de ces éléments pour vérifier si cet élément y est contenu. Les clés en double ne sont pas autorisées. Pause café #168.  Pourquoi remplacer les méthodes égales et hashcode en Java ?  - 2Lorsque nous mettons quelque chose dans un HashMap , la clé est stockée dans l'une de ces listes chaînées. La liste chaînée dans laquelle cette clé sera stockée est indiquée par le résultat de la méthode hashCode() pour cette clé. Autrement dit, si key1.hashCode() donne 4, alors cette key1 sera stockée dans le 4ème segment du tableau dans la LinkedList qui y existe . Par défaut, la méthode hashCode() renvoie des résultats différents pour chaque instance. Si nous avons un égal par défaut qui se comporte comme == , traitant toutes les instances en mémoire comme des objets différents, alors il n'y aura pas de problème. Comme vous vous en souvenez peut-être, dans notre exemple précédent, nous avons dit que nous souhaitions que les instances Person soient considérées comme égales si leurs âges et leurs noms sont identiques.
Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!
Créons maintenant une carte pour stocker ces instances sous forme de clés avec une chaîne spécifique comme paire de valeurs.
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
Dans la classe Person , nous n'avons pas remplacé la méthode hashCode , mais nous avons une méthode equals remplacée . Étant donné que le hashCode par défaut donne des résultats différents pour différentes instances Java de person1.hashCode() et person2.hashCode() , il y a de fortes chances d'obtenir des résultats différents. Notre carte peut se terminer avec différentes personnes dans différentes listes chaînées. Pause café #168.  Pourquoi remplacer les méthodes égales et hashcode en Java ?  - 3Cela va à l'encontre de la logique de HashMap . Après tout, un HashMap ne peut pas avoir plusieurs clés identiques ! Le fait est que le hashCode() par défaut hérité de la classe Object n'est pas suffisant. Même après avoir remplacé la méthode equals() de la classe Person . C'est pourquoi nous devons remplacer la méthode hashCode() après avoir remplacé la méthode equals . Maintenant, réparons cela. Nous devons remplacer notre méthode hashCode() afin qu'elle prenne en compte les mêmes champs que equals() , à savoir age et name .
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
@Override
public int hashCode() {
        int prime = 31;
        return prime*Objects.hash(name, age);
    }
Dans la méthode hashCode() , nous avons utilisé une valeur simple (vous pouvez utiliser n'importe quelle autre valeur). Cependant, il est suggéré d’utiliser des nombres premiers pour créer moins de problèmes. Essayons à nouveau de stocker ces clés dans notre HashMap :
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode() et person2.hashCode() seront identiques. Disons qu'ils sont 0. Le HashMap ira au segment 0 et la LinkedList y enregistrera person1 en tant que clé avec la valeur « 1 ». Dans le deuxième cas, lorsque HashMap retourne au bucket 0 pour stocker la clé person2 avec la valeur « 2 », il verra qu'une autre clé égale à celle-ci existe déjà là-bas. De cette façon, la clé précédente sera écrasée. Et seule la personne clé2 existera dans notre HashMap . C'est ainsi que nous avons appris le fonctionnement de la règle HashMap , qui stipule qu'on ne peut pas utiliser plusieurs clés identiques ! Cependant, gardez à l’esprit que des instances inégales peuvent avoir le même hashcode et que les instances égales doivent renvoyer le même hashcode.Pause café #168.  Pourquoi remplacer les méthodes égales et hashcode en Java ?  - 4
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION