Verstehe gleich und hashCode in Java

Dieses Dokument ist ein Nachdruck des Materials, das vor etwa 10 Jahren für neue Ingenieure erstellt wurde. Einige altmodische Teile wie API und Syntax mögen auffallen, aber sobald Sie die Idee kennen, beabsichtige ich, die grundlegenden Teile zu organisieren, die für eine lange Zeit verwendet werden können, und hoffe, dass sie jetzt für junge Ingenieure hilfreich sein werden.

Punkt des Verstehens

Voraussetzungen

Angenommen, die Benutzerklasse ist wie unten gezeigt vorhanden, lesen Sie weiter. Die Benutzerklasse behält ihre ID und ihren Namen als internen Status bei.

User.java


    class User {

        /** ID */
        private int id;

        /**Name*/
        private String name;

        /**
         *Es ist ein Konstruktor.
         * 
         * @param id ID
         * @Param Name Name
         */
        public User(int id, String name) {

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

        //Unten weggelassen
    }

Unterschied zwischen Identität und Äquivalenz

Was ist die "Identität" eines Objekts?

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

CapD20080626_1.png

Benutzer1 und Benutzer2 enthalten Verweise auf dasselbe Objekt. Daher sind die Objekte, auf die Benutzer1 und Benutzer2 verweisen, "identisch".

Was ist die "Äquivalenz" eines Objekts?

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

CapD20080626_2.png Benutzer3 und Benutzer5 haben dieselbe ID. Benutzer4 und Benutzer5 haben denselben Namen.

Daher sind Benutzer3 und Benutzer5 Objekte mit dem ID-Wert "gleicher Wert". Benutzer4 und Benutzer5 sind Objekte mit "gleichen Werten" in ihren Namenswerten.

Die Objektäquivalenz muss vom Programmierer selbst gemäß den Geschäftsregeln implementiert werden.

Die Objektäquivalenz wird in der Methode equals der entsprechenden Klasse definiert.

Wenn es eine Regel gibt, mit der die Äquivalenz des Benutzerklassenobjekts im ID-Wert beurteilt werden kann, beschreiben Sie den Prozess zum Vergleichen des ID-Werts in der Methode equals.

Wenn es eine Regel gibt, mit der die Äquivalenz des Objekts der Benutzerklasse im Namenswert beurteilt werden kann, beschreiben Sie den Prozess zum Vergleichen des Namenswerts in der Methode equals.

So bestimmen Sie die Identität eines Objekts

Die Objektidentität wird vom Vergleichsoperator "==" bestimmt. Da Benutzer1 und Benutzer2 oben dasselbe Objekt sind

    user1 == user2 // ⇒ true

Das Ergebnis von ist "wahr".

Weil Benutzer3, Benutzer4 und Benutzer5 unterschiedliche Objekte sind

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

Das Ergebnis von ist alles "falsch".

So bestimmen Sie die Äquivalenz von Objekten

Die Gleichheit von Objekten wird mit der in jeder Klasse implementierten "Gleichheits" -Methode verglichen.

Wenn die equals-Methode der User-Klasse mit ID als Vergleichsbedingung implementiert ist, lautet das Ergebnis der equals-Methode wie folgt.

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

Wenn die equals-Methode der User-Klasse mit dem Namen als Vergleichsbedingung implementiert ist, lautet das Ergebnis der equals-Methode wie folgt.

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

Implementierung der Equals-Methode

Implementierungsbeispiel für die Methode equals nach Benutzer-ID

Implementieren Sie die equals-Methode in der User-Klasse wie folgt, um die Objektäquivalenz der Benutzerklasse anhand der ID zu bestimmen.

User.java


    /**
      *Die Instanz dieser Klasse und das Objekt wurden als Argument übergeben
      *Gibt true zurück, wenn sie gleich sind.
      *Das als Argument übergebene Objekt ist eine Instanz der User-Klasse
      *Wenn die ID-Werte gleich sind, werden sie als gleich betrachtet.
      *
      * @True, wenn das im return-Argument übergebene Objekt eine Instanz der User-Klasse ist und die IDs gleich sind.
      */
    public boolean equals(Object other) {

        if (this == other) { //True, wenn das im Argument übergebene Objekt dieses Objekt selbst war
            return true;
        }

        if (!(other instanceof User)) { //Das als Argument übergebene Objekt ist ein Objekt der User-Klasse
            return false;               //Falsch wenn nicht.
        }

        User otherUser = (User) other;
        if (this.id == otherUser.getId()) { //Vergleichen Sie die ID-Werte, true wenn gleich, false wenn nicht gleich.
            return true;
        }
        return false;
    }

Implementierungsbeispiel für die Methode equals nach Benutzername (Name)

Implementieren Sie die equals-Methode in der User-Klasse wie folgt, um die Objektäquivalenz der Benutzerklasse anhand des Namens zu bestimmen.

Der Rest des Prozesses ist der gleiche, nur der Wertevergleich ändert sich von id zu name.

User.java


    /**
     *Die Instanz dieser Klasse und das Objekt wurden als Argument übergeben
     *Gibt true zurück, wenn sie gleich sind.
     *Das als Argument übergebene Objekt ist eine Instanz der User-Klasse
     *Wenn die Werte von name gleich sind, werden sie als gleich angesehen.
     * 
     * @True, wenn das im return-Argument übergebene Objekt eine Instanz der User-Klasse ist und die Namen gleich sind.
     */
    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;
    }

Verstehen Sie die hashCode-Methode

Was ist Hashcode?

Wenn Sie die Methode equals implementieren, müssen Sie auch die Methode hashCode implementieren. Die hashCode-Methode wird gemäß den folgenden Regeln implementiert.

Wenn Sie in der User-Klasse die equals-Methode implementieren, die die Gleichheit basierend auf dem ID-Wert ermittelt, wird die hashCode-Methode als Beispiel wie folgt implementiert.

Beispiel 1

User.java


    /**
     *Gibt den Hash-Code zurück.
     *
     * @Rückgabe des Hash-Werts einer Instanz dieser Klasse
     */
    public int hashCode() {
        return this.id;
    }

Da die equals-Methode die Äquivalenz basierend auf dem ID-Wert bestimmt, gibt die hashCode-Methode des Objekts, dessen ID-Werte gleich sind, dh die equals-Methode gibt true zurück, denselben Wert durch die obige Implementierung zurück. Es kann gesagt werden, dass die Verarbeitung der obigen hashCode-Methode für die obige Regel geeignet ist.

Beispiel 2

User.java


    /**
     *Gibt den Hash-Code zurück.
     * 
     * @Rückgabe des Hash-Werts einer Instanz dieser Klasse
     */
    public int hashCode() {
        return 0;
    }

Die Verarbeitung der obigen hashCode-Methode ist tatsächlich gültig. Der Hashcode für alle Objekte gibt 0 zurück. Das heißt, alle Objekte, für die die Methode equals true ist, geben denselben Wert (0) wie der Rückgabewert hashCode zurück. Die obige Implementierung wird auch im Lichte der Regeln der hashCode-Implementierung als in Ordnung angesehen, da Objekte, deren equals-Methode nicht true ist (das Ergebnis des Aufrufs der equals-Methode ist false), dieselbe hashCode-Methode zurückgeben können. ..

Die Implementierung in Beispiel 2 wird jedoch nicht empfohlen. Wenn Sie hashCode implementieren, implementieren Sie es mindestens wie in Beispiel 1.

Wenn hashCode nicht korrekt implementiert ist

Wenn Sie einer Instanz einer Klasse, die einen Hash-Algorithmus verwendet (HashMap, HashSet usw.), ein Objekt hinzufügen, das hashCode nicht korrekt implementiert, wird das erwartete Verhalten nicht erhalten.

Was ist ein Hash-Algorithmus?

Es ist ein Algorithmus, der die Verarbeitung rationalisiert, indem der Hash-Wert beim Speichern und Suchen nach Objekten im Voraus berechnet wird und Objekte basierend auf dem erhaltenen Hash-Wert gespeichert und gesucht werden.

Um den Hash-Algorithmus zu verstehen, empfiehlt es sich, die Verhaltensunterschiede zwischen ArrayList und HashSet zu vergleichen.

ArrayList und HashSet sind beide Klassen, die die Collection-Schnittstelle implementieren, weisen jedoch die folgenden Verhaltensunterschiede auf. (HashSet implementiert einen Hash-Algorithmus.)

Für ArrayList

CapD20080627_1.png

ArrayList speichert und hält die hinzugefügten Elemente in einer einspaltigen Liste. Beim Abrufen der gespeicherten Elemente wird die Methode equals des Objekts in der Reihenfolge vom Anfang der Liste aufgerufen, und das Element, für das das Ergebnis des Methodenaufrufs equals true ist, wird als Rückgabewert zurückgegeben.

⇒ Wenn die Anzahl der Elemente in der Liste sehr groß ist und sich das Element, das Sie abrufen möchten, hinter der Liste befindet, kann sich die Sucheffizienz erheblich verschlechtern.

Für HashSet

CapD20080627_2.png

HashSet speichert und ruft die hinzugefügten Elemente gemäß dem folgenden Verfahren ab.

  1. Rufen Sie die hashCode-Methode des hinzuzufügenden Objekts auf.
  2. Klassifizieren Sie das Objekt für jeden Rückgabewert der hashCode-Methode in "Räume" (der Implementierungsbegriff der HashSet-Klasse lautet Bucket) und speichern Sie das Zielobjekt mit der hashCode-Raumnummer im "Raum".
  3. Wenn Sie ein Element extrahieren, suchen Sie zuerst nach dem Zielobjekt in dem "Raum", dessen HashCode-Wert die Raumnummer basierend auf dem HashCode-Wert hat.
  4. Rufen Sie die Methode equals auf, um die in Schritt 3 in dem "Raum" gespeicherten Objekte zu speichern, und geben Sie als Rückgabewert das Element zurück, für das das Ergebnis des Aufrufs der Methode equals wahr ist.

Da die Elemente im Voraus basierend auf hashCode in "Räume" klassifiziert und gespeichert werden, muss beim Vergleichen von Objekten nur eine begrenzte Anzahl von Objekten verglichen werden, was die Sucheffizienz verbessert.

Wenn hashCode nicht korrekt implementiert ist, kann es vorkommen, dass das Zielobjekt nicht gefunden wird, da es in einem anderen Raum nach dem Zielobjekt sucht, obwohl die Objekte denselben Wert haben.

Wenn hashCode implementiert ist, um 0 zurückzugeben, werden alle Objekte im "Raum" mit der tatsächlichen Raumnummer 0 gespeichert, und der Algorithmus entspricht dem Hinzufügen eines Elements zu ArrayList, was den Vorteil des Hash-Algorithmus hat. Ich kann es nicht verstehen

Die Identität von equals und hashCode

Wenn Sie die Methoden equals und hashCode nicht implementieren

Die Methoden equals und hashCode sind ursprünglich in der Object-Klasse implementiert.

Die Object-Klasse ist eine Oberklasse aller Klassen. Wenn Sie die Methode equals oder hashCode für eine Instanz einer Klasse aufrufen, die die Methoden equals und hashCode nicht implementiert, wird sie daher in der übergeordneten Object-Klasse definiert (sofern keine andere erbende Klasse vorhanden ist). Die Methode equals und die Methode hashCode werden aufgerufen.

Wenn beispielsweise die Methode equals für eine Instanz einer Klasse aufgerufen wird, die die Methode equals nicht implementiert, lautet die Reihenfolge des Aufrufs der Methoden wie folgt.

  1. Die Methode equals wird aufgerufen.
  2. Da die Methode equals nicht in der Klasse der entsprechenden Instanz definiert ist, suchen Sie rückwirkend, wenn sie nicht in der übergeordneten Klasse definiert ist.
  3. Kehren Sie zur übergeordneten Klasse zurück. Wenn eine Klasse eine Methode equals definiert, wird diese Methode equals aufgerufen.
  4. Wenn Sie zur übergeordneten Klasse zurückkehren und in keiner der Klassen eine Gleichheitsmethode definiert ist, erreichen Sie schließlich die Definition der Objektklasse, und die in der Objektklasse definierte Gleichheitsmethode wird aufgerufen.

Was es bedeutet, die Methoden equals und hashCode zu implementieren

"Implementieren der Methode equals und der Methode hashCode" bedeutet (wenn keine andere Klasse zu erben ist)

Es bedeutet das.

Das Verhalten der Standardmethode entspricht der in der Objektklasse definierten Methode

Die in der Objektklasse definierte Methode equals bestimmt die "Objektäquivalenz" basierend auf der "Objektidentität".

Wenn Sie die equals-Methode in der User-Klasse nicht implementieren (wenn Sie die equals-Methode in der Object-Klasse nicht überschreiben), gilt die equals-Methode, wenn die Instanz in der User-Klasse weder eine ID noch ein Name ist und die Instanzen genau gleich sind. Wird wahr zurückkehren.

    //Verhalten der Methode der Objektklasse gleich
    Object obj1 = new Object();
    Object obj2 = new Object();
    Object obj3 = obj1;

    obj1 == obj2; // false
    obj2 == obj3; // false
    obj1 == obj3; // true
 
    // "=="Vergleich und Ergebnisse sind gleich
    obj1.equals(obj2); // false
    obj2.equals(obj3); // false
    obj1.equals(obj3); // true

    //Verhalten der Benutzerklasse, die keine Methode equals implementiert
    // (Da die in der Object-Klasse definierte Methode equals aufgerufen wird, ist das Verhalten dasselbe.)
    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

Verstehe gleich und hashCode in Java
[Java] HashCode und gleich Überschreibung
Empfehlung der Set-Operation durch Java (und Verständnis von Equals und HashCode)
[Java] Unterschied zwischen == und gleich
Beispiel für Codierung und Decodierung in Java
== und gleich
Informationen zu den Methoden equals () und hashcode ()
[Java-Anfänger] == Operator und Gleiche Methode
StringBuffer- und StringBuilder-Klasse in Java
Hallo Welt in Java und Gradle
Ich steckte in einer Falle fest, als ich meine eigenen Klassen equals und hashCode in Java mit IDE generierte
Unterschied zwischen final und Immutable in Java
Unterschied zwischen Arrylist und verknüpfter Liste in Java
Programmieren Sie PDF-Kopf- und Fußzeilen in Java
Konsoleneingabe in Java (Verständnis des Mechanismus)
Lernen Sie Flyweight-Muster und ConcurrentHashMap in Java
Die Richtung von Java in "C ++ Design and Evolution"
Von Java nach C und von C nach Java in Android Studio
Lesen und Schreiben von GZIP-Dateien in Java
Unterschied zwischen int und Integer in Java
Diskriminierung von Enum in Java 7 und höher
Änderungen in Java 11
Janken in Java
Java und JavaScript
XXE und Java
Umfangsrate in Java
FizzBuzz in Java
In Bezug auf transiente Modifikatoren und Serialisierung in Java
Erkennen Sie ähnliche Videos in Java und OpenCV Version 2
Parallele und parallele Verarbeitung in verschiedenen Sprachen (Java Edition)
Unterschied zwischen next () und nextLine () in Java Scanner
Unterschiede beim Schreiben von Java-, C # - und Javascript-Klassen
Erfassen und speichern Sie die Selen-Installation in Java
Erkennen Sie ähnliche Videos in Java und OpenCV Version 3
Hinzufügen, Lesen und Löschen von Excel-Kommentaren mit Java
Überprüfen Sie das statische und öffentliche Verhalten in Java-Methoden
[Java] Verstehe in 10 Minuten! Assoziatives Array und HashMap
Unterscheiden Sie in Java zwischen positiven und negativen Zahlen
Java fügt Wasserzeichen in Word-Dokumenten hinzu und entfernt sie
Erkennen Sie ähnliche Videos in Java und OpenCV Version 1
Repräsentiert "nächster Tag" und "vorheriger Tag" in Java / Android
Zum Verständnis von Karte und Flatmap in Stream (1)
Fragen in Java-Ausnahmebehandlung werfen und versuchen-fangen
Laden Sie Notizen in Java auf S3 hoch und laden Sie sie herunter
Verschlüsseln / Entschlüsseln mit AES256 in PHP und Java
Generieren Sie OffsetDateTime aus Clock und LocalDateTime in Java
Punkte, die bei Java beachtet werden müssen, sind gleich
[Java] Unterschied zwischen gleich und == in einer Zeichenfolge, die ein Referenztyp ist
Lesen Sie JSON in Java
Machen Sie einen Blackjack mit Java
[Android / Java] Bildschirmübergang und Rückgabeverarbeitung in Fragmenten
Konvertieren Sie JSON und YAML in Java (mit Jackson und SnakeYAML)
Einschränkungsprogrammierung in Java
Setzen Sie Java8 in Centos7
Schreiben Sie ABNF in Java und geben Sie die E-Mail-Adresse weiter
NVL-artiger Typ in Java
Verbinden Sie Arrays in Java