Comprendre equals et hashCode en Java

Ce document est une réimpression du matériel créé pour les nouveaux ingénieurs il y a environ 10 ans. Certaines parties démodées telles que l'API et la syntaxe peuvent être perceptibles, mais une fois que vous avez compris l'idée, j'ai l'intention d'organiser les parties de base qui peuvent être utilisées pendant longtemps, j'espère donc que cela sera utile pour les jeunes ingénieurs maintenant.

Point de compréhension

--Comprendre la différence entre l'opérateur d'égalité "==" et la méthode equals en Java

Conditions préalables

En supposant que la classe User existe comme indiqué ci-dessous, lisez la suite. La classe User conserve son ID et son nom en tant qu'état interne.

User.java


    class User {

        /** ID */
        private int id;

        /**Nom*/
        private String name;

        /**
         *C'est un constructeur.
         * 
         * @param id ID
         * @nom du paramètre nom
         */
        public User(int id, String name) {

            this.id = id;
            this.name = name;
        }

        //Omis ci-dessous
    }

Différence entre identité et équivalence

Quelle est «l'identité» d'un objet?

    User user1 = new User(1, "Tanaka");
    User user2 = user1;

CapD20080626_1.png

user1 et user2 contiennent des références au même objet. Par conséquent, les objets référencés par user1 et user2 sont "identiques".

Quelle est l '«équivalence» d'un objet?

    User user3 = new User(1, "Tanaka");
    User user4 = new User(2, "Suzuki");
    User user5 = new User(1, "Suzuki");

CapD20080626_2.png user3 et user5 ont le même ID. user4 et user5 ont le même nom.

Par conséquent, user3 et user5 sont des objets avec "valeur égale" dans la valeur d'ID. user4 et user5 sont des objets avec des «valeurs égales» dans leurs valeurs de nom.

L'équivalence d'objet doit être implémentée par le programmeur lui-même selon les règles métier.

L'équivalence d'objet est définie dans la méthode equals de la classe appropriée.

S'il existe une règle pour juger de l'équivalence de l'objet de classe User dans la valeur ID, décrivez le processus de comparaison de la valeur ID dans la méthode equals.

S'il existe une règle pour juger de l'équivalence de l'objet de la classe User dans la valeur de nom, décrivez le processus pour comparer la valeur de nom dans la méthode equals.

Comment déterminer l'identité d'un objet

L'identité de l'objet est déterminée par l'opérateur de comparaison "==". Puisque user1 et user2 ci-dessus sont le même objet

    user1 == user2 // ⇒ true

Le résultat de est "vrai".

Parce que user3, user4 et user5 sont tous des objets différents

    user3 == user4 // ⇒ false
    user3 == user5 // ⇒ false
    user4 == user5 // ⇒ false

Le résultat de est tout "faux".

Comment déterminer l'équivalence des objets

L'égalité des objets est comparée par la méthode "equals" implémentée dans chaque classe.

Si la méthode equals de la classe User est implémentée avec l'ID comme condition de comparaison, le résultat de la méthode equals est le suivant.

    user3.equals(user4) // ⇒ false
    user3.equals(user5) // ⇒ true
    user4.equals(user5) // ⇒ false

Si la méthode equals de la classe User est implémentée avec le nom comme condition de comparaison, le résultat de la méthode equals est le suivant.

    user3.equals(user4) // ⇒ false
    user3.equals(user5) // ⇒ false
    user4.equals(user5) // ⇒ true

Mise en œuvre de la méthode égale

Exemple d'implémentation de la méthode equals par ID utilisateur

Pour déterminer l'équivalence d'objet de la classe User par ID, implémentez la méthode equals dans la classe User comme suit.

User.java


    /**
      *L'instance de cette classe et l'objet passé en argument
      *Renvoie vrai s'ils sont égaux.
      *L'objet passé en argument est une instance de la classe User
      *Si les valeurs id sont égales, elles sont considérées comme égales.
      *
      * @Vrai si l'objet passé dans l'argument de retour est une instance de la classe User et que les identifiants sont égaux.
      */
    public boolean equals(Object other) {

        if (this == other) { //Vrai si l'objet passé dans l'argument était cet objet lui-même
            return true;
        }

        if (!(other instanceof User)) { //L'objet passé en argument est un objet de la classe User
            return false;               //Faux sinon.
        }

        User otherUser = (User) other;
        if (this.id == otherUser.getId()) { //Comparez les valeurs d'ID, vrai si égal, faux si différent.
            return true;
        }
        return false;
    }

Exemple d'implémentation de la méthode equals par nom d'utilisateur (nom)

Pour déterminer l'équivalence d'objet de la classe User par son nom, implémentez la méthode equals dans la classe User comme suit.

Le reste du processus est le même, seule la comparaison des valeurs change de l'id au nom.

User.java


    /**
     *L'instance de cette classe et l'objet passé en argument
     *Renvoie vrai s'ils sont égaux.
     *L'objet passé en argument est une instance de la classe User
     *Si les valeurs de name sont égales, elles sont considérées comme égales.
     * 
     * @True si l'objet passé dans l'argument de retour est une instance de la classe User et que les noms sont égaux.
     */
    public boolean equals(Object other) {

        if (this == other) {
            return true;
        }

        if (!(other instanceof User)) {
            return false;
        }

        User otherUser = (User) other;
        if (this.name.equals(otherUser.getName())) { 
            return true;
        }
        return false;
    }

Comprendre la méthode hashCode

Qu'est-ce que hashCode

Si vous implémentez la méthode equals, vous devez également implémenter la méthode hashCode. La méthode hashCode est implémentée selon les règles suivantes.

Dans la classe User, si vous implémentez la méthode equals qui détermine l'égalité en fonction de la valeur de l'ID, la méthode hashCode est implémentée comme suit, à titre d'exemple.

Exemple 1

User.java


    /**
     *Renvoie le code de hachage.
     *
     * @renvoie la valeur de hachage d'une instance de cette classe
     */
    public int hashCode() {
        return this.id;
    }

Dans la méthode equals, l'équivalence est évaluée en fonction de la valeur de l'ID, de sorte que la méthode hashCode de l'objet dont les valeurs d'ID sont égales, c'est-à-dire que la méthode equals renvoie true, retournera la même valeur par l'implémentation ci-dessus. , On peut dire que le traitement de la méthode hashCode ci-dessus est approprié pour la règle ci-dessus.

Exemple 2

User.java


    /**
     *Renvoie le code de hachage.
     * 
     * @renvoie la valeur de hachage d'une instance de cette classe
     */
    public int hashCode() {
        return 0;
    }

Le traitement de la méthode hashCode ci-dessus est en fait valide. Le hashCode pour tous les objets renvoie 0. Autrement dit, tous les objets pour lesquels la méthode equals est true renverront la même valeur (0) que la valeur de retour hashCode. L'implémentation ci-dessus est également considérée comme correcte à la lumière des règles de l'implémentation de hashCode, car il est acceptable pour les objets dont la méthode equals n'est pas vraie (le résultat de l'appel de la méthode equals est faux) de renvoyer la même méthode hashCode. ..

Cependant, la mise en œuvre de l'exemple 2 n'est pas recommandée. Lors de l'implémentation de hashCode, implémentez-le au moins comme dans l'exemple 1.

Si hashCode n'est pas implémenté correctement

Si vous ajoutez un objet qui n'implémente pas correctement hashCode à une instance d'une classe qui utilise un algorithme de hachage (HashMap, HashSet, etc.), le comportement attendu ne sera pas obtenu.

Qu'est-ce qu'un algorithme de hachage?

Il s'agit d'un algorithme qui rationalise le traitement en calculant la valeur de hachage à l'avance lors du stockage et de la recherche d'objets, et du stockage et de la recherche d'objets en fonction de la valeur de hachage obtenue.

Pour comprendre l'algorithme de hachage, c'est une bonne idée de comparer les différences de comportement entre ArrayList et HashSet.

ArrayList et HashSet sont tous deux des classes qui implémentent l'interface Collection, mais ils présentent les différences de comportement suivantes. (HashSet implémente un algorithme de hachage.)

Pour ArrayList

CapD20080627_1.png

ArrayList stocke et contient les éléments ajoutés dans une liste à une colonne. Lors de la récupération des éléments stockés, la méthode equals de l'objet est appelée dans l'ordre depuis le début de la liste, et l'élément pour lequel le résultat de l'appel de méthode equals est vrai est renvoyé comme valeur de retour.

⇒ Si le nombre d'éléments dans la liste est énorme et que l'élément que vous voulez récupérer est derrière la liste, l'efficacité de la recherche peut être extrêmement détériorée.

Pour HashSet

CapD20080627_2.png

HashSet stocke et récupère les éléments ajoutés selon la procédure suivante.

  1. Appelez la méthode hashCode de l'objet à ajouter.
  2. Classez l'objet en "salles" (le terme d'implémentation de la classe HashSet est Bucket) pour chaque valeur de retour de la méthode hashCode et stockez l'objet cible dans la "salle" avec le numéro de salle de hashCode.
  3. Lors de l'extraction d'un élément, recherchez d'abord l'objet cible dans la "salle" qui a la valeur hashCode comme numéro de pièce en fonction de la valeur hashCode.
  4. Appelez la méthode equals pour les objets stockés dans la "pièce" trouvée à l'étape 3 et renvoyez l'élément pour lequel le résultat de l'appel de la méthode equals est vrai comme valeur de retour.

Étant donné que les éléments sont classés dans des "pièces" en fonction de hashCode à l'avance et stockés, lors de la comparaison d'objets, il est seulement nécessaire de comparer un nombre limité d'objets, ce qui améliore l'efficacité de la recherche.

Si hashCode n'est pas implémenté correctement, il se peut que l'objet cible ne puisse pas être trouvé car l'objet cible est recherché dans une autre pièce même si les objets ont la même valeur.

Si hashCode est implémenté pour retourner 0, tous les objets seront stockés dans la "salle" avec pratiquement le numéro de salle 0, et l'algorithme sera le même que l'ajout d'un élément à ArrayList, qui a l'avantage de l'algorithme de hachage Je ne peux pas comprendre.

L'identité des égaux et hashCode

Si vous n'implémentez pas les méthodes equals et hashCode

Les méthodes equals et hashCode sont à l'origine implémentées dans la classe Object.

La classe Object est une superclasse de toutes les classes. Par conséquent, si vous appelez la méthode equals ou hashCode sur une instance d'une classe qui n'implémente pas les méthodes equals et hashCode, elle sera définie dans la classe Object parente (à moins qu'il n'y ait pas d'autre classe héritière). La méthode equals et la méthode hashCode sont appelées.

Par exemple, si la méthode equals est appelée pour une instance d'une classe qui n'implémente pas la méthode equals, l'ordre d'appel des méthodes est le suivant.

  1. La méthode equals est appelée.
  2. La méthode equals n'étant pas définie dans la classe de l'instance correspondante, elle est recherchée rétroactivement si elle n'est pas définie dans la classe parent.
  3. Revenez à la classe parente, et si une classe définit une méthode equals, cette méthode equals est appelée.
  4. Si vous revenez à la classe parente et qu'aucune méthode equals n'est définie dans l'une des classes, vous atteindrez finalement la définition de la classe Object et la méthode equals définie dans la classe Object sera appelée.

Ce que signifie implémenter les méthodes equals et hashCode

"Implémentation de la méthode equals et de la méthode hashCode" signifie (quand il n'y a pas d'autre classe à hériter)

Cela signifie que.

Comportement de la méthode par défaut equals définie dans la classe Object

La méthode equals définie dans la classe Object détermine "l'équivalence d'objet" basée sur "l'identité d'objet".

Par conséquent, si vous n'implémentez pas la méthode equals dans la classe User (sans remplacer la méthode equals dans la classe Object), la méthode equals si l'instance de la classe User n'est ni un ID ni un nom et que les instances sont exactement les mêmes (identiques). Reviendra vrai.

    //Comportement de la méthode equals de la classe Object
    Object obj1 = new Object();
    Object obj2 = new Object();
    Object obj3 = obj1;

    obj1 == obj2; // false
    obj2 == obj3; // false
    obj1 == obj3; // true
 
    // "=="La comparaison et les résultats sont les mêmes
    obj1.equals(obj2); // false
    obj2.equals(obj3); // false
    obj1.equals(obj3); // true

    //Comportement de la classe User qui n'implémente pas la méthode equals
    // (Le comportement est le même car la méthode equals définie dans la classe Object est appelée.)
    User user1 = new User();
    User user2 = new User();
    User user3 = user1;

    user1 == user2; // false
    user2 == user3; // false
    user1 == user3; // true

    user1.equals(user2); // false
    user2.equals(user3); // false
    user1.equals(user3); // true

Recommended Posts

Comprendre equals et hashCode en Java
[Java] HashCode et remplacement égal
Recommandation de l'opération set par Java (et compréhension de equals et hashCode)
[Java] Différence entre == et égal
Exemple d'encodage et de décodage en Java
== et égal
À propos des méthodes equals () et hashcode ()
[Java débutant] == opérateur et méthode equals
Classe StringBuffer et StringBuilder en Java
Bonjour tout le monde en Java et Gradle
J'ai été piégé lorsque j'ai généré mes propres égaux de classe et hashCode en Java à l'aide de l'IDE
Différence entre final et immuable en Java
Différence entre les listes d'arry et les listes liées en Java
Programmer les en-têtes et pieds de page PDF en Java
Entrée de la console en Java (comprendre le mécanisme)
Apprenez les modèles Flyweight et ConcurrentHashMap en Java
La direction de Java dans "C ++ Design and Evolution"
De Java à C et de C à Java dans Android Studio
Lire et écrire des fichiers gzip en Java
Différence entre int et Integer en Java
Discrimination d'énum dans Java 7 et supérieur
Changements dans Java 11
Janken à Java
Java et JavaScript
XXE et Java
Taux circonférentiel à Java
FizzBuzz en Java
Concernant les modificateurs transitoires et la sérialisation en Java
Détecter des vidéos similaires dans Java et OpenCV rev.2
Traitement parallèle et parallèle dans divers langages (édition Java)
Différence entre next () et nextLine () dans Java Scanner
Différences dans l'écriture des classes Java, C # et Javascript
Capture et sauvegarde de l'installation de sélénium en Java
Détecter des vidéos similaires dans Java et OpenCV rev.3
Ajouter, lire et supprimer des commentaires Excel à l'aide de Java
Vérifier le comportement statique et public dans les méthodes Java
[Java] Comprenez en 10 minutes! Tableau associatif et HashMap
Distinguer les nombres positifs et négatifs en Java
Java ajoute et supprime les filigranes dans les documents Word
Détecter des vidéos similaires dans Java et OpenCV rev.1
Représente le «jour suivant» et le «jour précédent» en Java / Android
Vers la compréhension de la carte et de la flatmap dans Stream (1)
Questions sur la gestion des exceptions Java throw et try-catch
Télécharger et télécharger des notes en java sur S3
Crypter / décrypter avec AES256 en PHP et Java
Générer OffsetDateTime à partir de Clock et LocalDateTime en Java
Points à connaître avec Java Equals
[Java] Différence entre equals et == dans une chaîne de caractères qui est un type de référence
Lire JSON en Java
Faites un blackjack avec Java
[Android / Java] Transition d'écran et traitement de retour par fragments
Convertir JSON et YAML en Java (en utilisant Jackson et SnakeYAML)
Programmation par contraintes en Java
Mettez java8 dans centos7
Écrivez ABNF en Java et transmettez l'adresse e-mail
NVL-ish guy en Java
Joindre des tableaux en Java