Warum muss ich die Methoden equals und hashCode in Java überschreiben?

383

Kürzlich habe ich dieses Developer Works-Dokument gelesen .

Das Dokument ist über die Definition hashCode()und equals()effektiv und richtig, aber ich nicht in der Lage bin, um herauszufinden , warum wir diese beiden Methoden außer Kraft setzen müssen.

Wie kann ich die Entscheidung treffen, diese Methoden effizient umzusetzen?

Shashi
quelle
4
Es gibt zwei großartige Artikel auf programming.guide, die genau dies erklären: Wann sollte ich gleich überschreiben? und warum Sie hashCode immer überschreiben sollten, wenn Sie equals überschreiben . (Warnung, die akzeptierte Antwort ist tatsächlich falsch.)
Aioobe
Fallüberschreibung ist nur gleich: Zwei gleiche Objekte haben unterschiedlichen Hashcode = gleiche Objekte werden in unterschiedlichen Buckets abgelegt (Duplizierung). Nur Hashcode überschreiben: Zwei gleiche Objekte haben denselben Hashcode = dasselbe Objekt wird in denselben Bucket verschoben (Duplizierung).
VdeX

Antworten:

524

Joshua Bloch sagt über effektives Java

Sie müssen hashCode () in jeder Klasse überschreiben, die equals () überschreibt. Andernfalls wird der allgemeine Vertrag für Object.hashCode () verletzt, wodurch verhindert wird, dass Ihre Klasse in Verbindung mit allen Hash-basierten Sammlungen, einschließlich HashMap, HashSet und Hashtable, ordnungsgemäß funktioniert.

Versuchen wir es anhand eines Beispiels zu verstehen, was passieren würde, wenn wir überschreiben, equals()ohne es zu überschreiben, hashCode()und versuchen, a zu verwenden Map.

Angenommen, wir haben eine Klasse wie diese und zwei Objekte von MyClasssind gleich, wenn sie importantFieldgleich sind (mit hashCode()und equals()durch Eclipse erzeugt).

public class MyClass {

    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    public String getEqualField() {
        return importantField;
    }

    public String getAnotherField() {
        return anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }

}

Nur überschreiben equals

Wenn nur equalsüberschrieben wird, wird beim myMap.put(first,someValue)ersten Aufruf ein Hash in einen Bucket und beim Aufruf myMap.put(second,someOtherValue)in einen anderen Bucket gehasht (da diese einen anderen haben hashCode). Obwohl sie gleich sind, da sie nicht zum selben Bucket hashen, kann die Karte dies nicht erkennen und beide bleiben auf der Karte.


Obwohl es nicht notwendig ist, zu überschreiben, equals()wenn wir überschreiben hashCode(), wollen wir sehen, was in diesem speziellen Fall passieren würde, wenn wir wissen, dass zwei Objekte von MyClassgleich sind, wenn sie importantFieldgleich sind, aber wir nicht überschreiben equals().

Nur überschreiben hashCode

Stellen Sie sich vor, Sie haben das

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

Wenn Sie nur überschreiben , dauert es hashCodebeim Aufrufen myMap.put(first,someValue)zuerst, berechnet es hashCodeund speichert es in einem bestimmten Bucket. Wenn Sie myMap.put(second,someOtherValue)es dann aufrufen , sollte es gemäß der Kartendokumentation zuerst durch das zweite ersetzt werden, da sie gleich sind (entsprechend den Geschäftsanforderungen).

Das Problem ist jedoch, dass equals nicht neu definiert wurde. Wenn also die Karte seconddurch den Bucket hasht und iteriert, um zu sehen, ob es ein Objekt gibt k, second.equals(k)das wahr ist, wird es keines finden, wie second.equals(first)es sein wird false.

Hoffe es war klar

Lombo
quelle
5
Können Sie bitte im zweiten Fall etwas näher erläutern, warum das zweite Objekt in einen anderen Eimer gelangen muss?
Hussain Akhtar Wahid 'Ghouri'
57
Diese Antwort gefällt mir nicht, weil sie darauf hindeutet, dass Sie hashCode () nicht überschreiben können, ohne equals () zu überschreiben, was einfach nicht stimmt. Sie sagen , dass Ihr Beispiel - Code (der „außer Kraft setzt nur hashCode“ Teil) wird nicht funktionieren , weil Sie definieren Ihre zwei Objekte als gleich, aber - sorry - diese Definition nur in Ihrem Kopf ist. In Ihrem ersten Beispiel haben Sie zwei ungleiche Objekte mit demselben Hashcode, und das ist völlig legal. Der Grund, warum Sie equals () überschreiben müssen, liegt nicht darin, dass Sie hashCode () bereits überschrieben haben, sondern darin, dass Sie Ihre Definition "equals" von Ihrem Kopf in den Code verschieben möchten.
user2543253
11
if you think you need to override one, then you need to override both of themist falsch. Sie müssen überschreiben, hashCodewenn Ihre Klasse überschreibt, equalsaber umgekehrt ist nicht wahr.
akhil_mittal
4
Ich denke, es ist völlig in Ordnung, nur hashCode () zu überschreiben, ohne auch equals () zu überschreiben. Es ist auch was in Effective Java geschrieben ist : books.google.fr/…
Johnny
2
@PhantomReference, beachten Sie, dass nur zwingende equalswürde den Vertrag verletzen im javadoc von buchstabiert Object: „Wenn zwei Objekte gleich nach dem sind equals(Object)Verfahren, dann den Aufruf hashCode. Verfahren auf jeder der beiden Objekte müssen die gleiche ganzzahlige Ergebnis produzieren“ Sicher, nicht alle Teile aller Verträge werden in allen Codes ausgeübt, aber formal gesehen handelt es sich dennoch um einen Verstoß, und ich würde es als einen Fehler betrachten, der darauf wartet, passiert zu werden.
Aioobe
264

Sammlungen wie HashMapund HashSetverwenden einen Hashcode- Wert eines Objekts, um zu bestimmen, wie es in einer Sammlung gespeichert werden soll, und der Hashcode wird erneut verwendet, um das Objekt in seiner Sammlung zu lokalisieren.

Das Hashing-Abrufen erfolgt in zwei Schritten:

  1. Finde den richtigen Eimer (mit hashCode())
  2. Durchsuche den Eimer nach dem richtigen Element (mit equals())

Hier ist ein kleines Beispiel, warum wir überschreiben sollten equals()und hashcode().

Stellen Sie sich eine EmployeeKlasse vor, die zwei Felder enthält: Alter und Name.

public class Employee {

    String name;
    int age;

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof Employee))
            return false;
        Employee employee = (Employee) obj;
        return employee.getAge() == this.getAge()
                && employee.getName() == this.getName();
    }

    // commented    
    /*  @Override
        public int hashCode() {
            int result=17;
            result=31*result+age;
            result=31*result+(name!=null ? name.hashCode():0);
            return result;
        }
     */
}

Erstellen Sie nun eine Klasse, fügen Sie ein EmployeeObjekt in ein ein HashSetund testen Sie, ob dieses Objekt vorhanden ist oder nicht.

public class ClientTest {
    public static void main(String[] args) {
        Employee employee = new Employee("rajeev", 24);
        Employee employee1 = new Employee("rajeev", 25);
        Employee employee2 = new Employee("rajeev", 24);

        HashSet<Employee> employees = new HashSet<Employee>();
        employees.add(employee);
        System.out.println(employees.contains(employee2));
        System.out.println("employee.hashCode():  " + employee.hashCode()
        + "  employee2.hashCode():" + employee2.hashCode());
    }
}

Es wird Folgendes gedruckt:

false
employee.hashCode():  321755204  employee2.hashCode():375890482

Führen Sie nun die Kommentierungsmethode aus hashcode(), und führen Sie die folgende Ausgabe aus:

true
employee.hashCode():  -938387308  employee2.hashCode():-938387308

Können Sie nun sehen, warum, wenn zwei Objekte als gleich betrachtet werden, auch ihre Hashcodes gleich sein müssen? Andernfalls könnten Sie das Objekt nie finden, da die Standard- Hashcode- Methode in der Klasse Object praktisch immer eine eindeutige Nummer für jedes Objekt liefert , selbst wenn die equals()Methode so überschrieben wird, dass zwei oder mehr Objekte als gleich angesehen werden . Es spielt keine Rolle, wie gleich die Objekte sind, wenn ihre Hashcodes dies nicht widerspiegeln. Also noch einmal: Wenn zwei Objekte gleich sind, müssen auch ihre Hashcodes gleich sein.

Rajeev Pani ..
quelle
4
Perfektes Beispiel. Der Unterschied wurde deutlich!
Coderpc
3
schön erklärt @rajeev
VdeX
2
@VikasVerma gleich Objekt hat gleichen Hashcode bedeutet nicht, dass ungleiches Objekt ungleichen Hashcode hat. Was ist, wenn Objekte tatsächlich unterschiedlich sind, ihr Hashcode jedoch gleich ist?
Ravi
1
sehr schön erklärt :)
Rahul
4
viel bessere Antwort als die akzeptierte Antwort! Danke
Treibholz
50

Sie müssen hashCode () in jeder Klasse überschreiben, die equals () überschreibt. Andernfalls wird der allgemeine Vertrag für Object.hashCode () verletzt, wodurch verhindert wird, dass Ihre Klasse in Verbindung mit allen Hash-basierten Sammlungen, einschließlich HashMap, HashSet und Hashtable, ordnungsgemäß funktioniert.


   aus Effective Java von Joshua Bloch

Durch die Definition equals()und hashCode()Konsistenz können Sie die Benutzerfreundlichkeit Ihrer Klassen als Schlüssel in Hash-basierten Sammlungen verbessern. Wie das API-Dokument für hashCode erklärt: "Diese Methode wird zugunsten von Hashtabellen wie den von unterstützt java.util.Hashtable."

Die beste Antwort auf Ihre Frage, wie Sie diese Methoden effizient implementieren können, besteht darin, Kapitel 3 von Effective Java zu lesen .

JuanZe
quelle
4
Dies ist die richtige Antwort. Folglich ist es natürlich egal, dass Sie die Klasse nicht implementiert haben, wenn Sie sie nie in einer Hash-basierten Sammlung verwenden hashCode().
schlank
1
In komplexeren Fällen wissen Sie nie, ob die von Ihnen verwendeten Sammlungen Hashes verwenden. Vermeiden Sie daher "es spielt keine Rolle, dass Sie hashCode () nicht implementiert haben"
Victor Sergienko
1
Kann ich hashCode () überschreiben, ohne equals () zu überschreiben?
Johnny
@StasS, ja, entgegen der akzeptierten Antwort. Siehe Erklärung im zweiten Teil dieses Artikels: Warum Sie
HashCode
22

Einfach ausgedrückt, die equals-Methode in Object prüft auf Referenzgleichheit, wobei zwei Instanzen Ihrer Klasse bei gleichen Eigenschaften immer noch semantisch gleich sein können. Dies ist beispielsweise wichtig, wenn Sie Ihre Objekte in einen Container legen, der Equals und Hashcode verwendet, wie z. B. HashMap und Set . Nehmen wir an, wir haben eine Klasse wie:

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }
}

Wir erstellen zwei Instanzen mit derselben ID :

Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");

Ohne überschreibende Gleichheit erhalten wir:

  • a.equals (b) ist falsch, da es sich um zwei verschiedene Instanzen handelt
  • a.equals (a) ist wahr, da es sich um dieselbe Instanz handelt
  • b.equals (b) ist wahr, da es sich um dieselbe Instanz handelt

Richtig? Na vielleicht, wenn du das willst. Angenommen, wir möchten, dass Objekte mit derselben ID dasselbe Objekt sind, unabhängig davon, ob es sich um zwei verschiedene Instanzen handelt. Wir überschreiben die Gleichheit (und den Hashcode):

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Foo) {
            return ((Foo)other).id.equals(this.id);   
        }
    }

    @Override
    public int hashCode() {
        return this.id.hashCode();
    }
}

Für die Implementierung von Equals und Hashcode kann ich die Verwendung der Hilfsmethoden von Guava empfehlen

Crunchdog
quelle
20

Identität ist nicht Gleichheit.

  • entspricht der ==Identität des Bedienertests .
  • equals(Object obj) Methode vergleicht Gleichheitstest (dh wir müssen Gleichheit durch Überschreiben der Methode feststellen)

Warum muss ich die Methoden equals und hashCode in Java überschreiben?

Zuerst müssen wir die Verwendung der Gleichheitsmethode verstehen.

Um Identitätsunterschiede zwischen zwei Objekten zu identifizieren, müssen wir die Methode equals überschreiben.

Zum Beispiel:

Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.

------------------------------
Now I have overriden Customer class equals method as follows:
 @Override
    public boolean equals(Object obj) {
        if (this == obj)   // it checks references
            return true;
        if (obj == null) // checks null
            return false;
        if (getClass() != obj.getClass()) // both object are instances of same class or not
            return false;
        Customer other = (Customer) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference 
            return false;
        return true; 
    }
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2);  // returns true by our own logic

Jetzt kann die hashCode-Methode leicht verstehen.

hashCode erzeugt eine Ganzzahl, um Objekte in Datenstrukturen wie HashMap , HashSet zu speichern .

Angenommen, wir haben eine Override-Equals-Methode Customerwie oben beschrieben.

customer1.equals(customer2);  // returns true by our own logic

Während der Arbeit mit der Datenstruktur, wenn wir Objekte in Buckets speichern (Bucket ist ein ausgefallener Name für Ordner). Wenn wir die integrierte Hash-Technik verwenden, werden für mehr als zwei Kunden zwei verschiedene Hashcodes generiert. Wir speichern also dasselbe identische Objekt an zwei verschiedenen Orten. Um diese Art von Problemen zu vermeiden, sollten wir die hashCode-Methode auch basierend auf den folgenden Prinzipien überschreiben.

  • ungleiche Instanzen können denselben Hashcode haben.
  • Gleiche Instanzen sollten denselben Hashcode zurückgeben.
Premraj
quelle
3
Das habe ich seit 1 Stunde gesucht. Awesome Kumpel (y)
Adnan
13

Ok, lassen Sie mich das Konzept in sehr einfachen Worten erklären.

Erstens haben wir aus einer breiteren Perspektive Sammlungen, und Hashmap ist eine der Datenstrukturen in den Sammlungen.

Um zu verstehen, warum wir die Methode equals und hashcode überschreiben müssen, müssen wir zuerst verstehen, was Hashmap ist und was funktioniert.

Eine Hashmap ist eine Datenstruktur, in der Schlüsselwert-Datenpaare auf Array-Weise gespeichert werden. Sagen wir a [], wobei jedes Element in 'a' ein Schlüsselwertpaar ist.

Außerdem kann jeder Index in dem obigen Array eine verknüpfte Liste sein, wodurch mehr als ein Wert an einem Index vorliegt.

Warum wird nun eine Hashmap verwendet? Wenn wir in einem großen Array suchen müssen, dann durchsuchen Sie jedes Array, wenn es nicht effizient ist. Welche Hash-Technik sagt uns also, dass wir das Array mit einer gewissen Logik vorverarbeiten und die Elemente basierend auf dieser Logik gruppieren können, dh Hashing

Beispiel: Wir haben das Array 1,2,3,4,5,6,7,8,9,10,11 und wenden eine Hash-Funktion mod 10 an, sodass 1,11 zusammen gruppiert werden. Wenn wir also im vorherigen Array nach 11 suchen müssten, müssten wir das gesamte Array iterieren, aber wenn wir es gruppieren, begrenzen wir unseren Iterationsumfang, wodurch die Geschwindigkeit verbessert wird. Diese Datenstruktur, die zum Speichern aller oben genannten Informationen verwendet wird, kann der Einfachheit halber als 2D-Array betrachtet werden

Abgesehen von der obigen Hashmap wird nun auch angegeben, dass keine Duplikate hinzugefügt werden. Und dies ist der Hauptgrund, warum wir die Gleichheits- und Hashcodes überschreiben müssen

Wenn also gesagt wird, dass dies die interne Funktionsweise von Hashmap erklärt, müssen wir herausfinden, welche Methoden die Hashmap hat und wie sie den oben beschriebenen Regeln folgt

Daher hat die Hashmap eine Methode, die als put (K, V) bezeichnet wird, und gemäß der Hashmap sollte sie den obigen Regeln folgen, um das Array effizient zu verteilen und keine Duplikate hinzuzufügen

Put generiert also zuerst den Hashcode für den angegebenen Schlüssel, um zu entscheiden, in welchen Index der Wert eingegeben werden soll. Wenn an diesem Index nichts vorhanden ist, wird der neue Wert dort hinzugefügt, wenn dort bereits etwas vorhanden ist Dann sollte der neue Wert nach dem Ende der verknüpften Liste an diesem Index hinzugefügt werden. Denken Sie jedoch daran, dass gemäß dem gewünschten Verhalten der Hashmap keine Duplikate hinzugefügt werden sollten. Nehmen wir also an, Sie haben zwei Integer-Objekte aa = 11, bb = 11. Wie bei jedem von der Objektklasse abgeleiteten Objekt besteht die Standardimplementierung für den Vergleich zweier Objekte darin, dass die Referenz und nicht die Werte innerhalb des Objekts verglichen werden. Im obigen Fall bestehen beide, obwohl semantisch gleich, den Gleichheitstest nicht und es besteht die Möglichkeit, dass zwei Objekte mit demselben Hashcode und denselben Werten vorhanden sind, wodurch Duplikate erstellt werden. Wenn wir überschreiben, können wir das Hinzufügen von Duplikaten vermeiden. Sie könnten sich auch darauf beziehenDetailarbeiten

import java.util.HashMap;


public class Employee {

String name;
String mobile;
public Employee(String name,String mobile) {
    this.name=name;
    this.mobile=mobile;
}

@Override
public int hashCode() {
    System.out.println("calling hascode method of Employee");
    String str=this.name;
    Integer sum=0;
    for(int i=0;i<str.length();i++){
        sum=sum+str.charAt(i);
    }
    return sum;

}
@Override
public boolean equals(Object obj) {
    // TODO Auto-generated method stub
    System.out.println("calling equals method of Employee");
    Employee emp=(Employee)obj;
    if(this.mobile.equalsIgnoreCase(emp.mobile)){

        System.out.println("returning true");
        return true;
    }else{
        System.out.println("returning false");
        return false;
    }


}

public static void main(String[] args) {
    // TODO Auto-generated method stub

    Employee emp=new Employee("abc", "hhh");
    Employee emp2=new Employee("abc", "hhh");
    HashMap<Employee, Employee> h=new HashMap<>();
    //for (int i=0;i<5;i++){
        h.put(emp, emp);
        h.put(emp2, emp2);

    //}

    System.out.println("----------------");
    System.out.println("size of hashmap: "+h.size());


}

}
Chetan
quelle
Ich habe eine Verwirrung, warum müssen wir die Methode equals überschreiben, wenn wir die HashCode-Methode im Fall von HashMap überschreiben? In jedem Fall ersetzt Hashmap den Wert, wenn der Hashcode des Objekts gleich ist.
Vikas Verma
Die @ VikasVerma-Hashmap ersetzt keinen Wert, wenn der Hashcode der Objekte gleich ist. Sie entscheidet nur über den Index, in dem das neu hinzugefügte Objekt zur Hashmap platziert werden soll. Jetzt können sich Objekte im Index befinden. Um Doppelungen zu vermeiden, überschreiben wir die Methode equals und schreiben die Logik, um zu definieren, wann die beiden Objekte im Vergleich als gleich behandelt werden sollen. Wenn nicht überschrieben, werden Objekte mit denselben Werten gespeichert, da die Referenz beider Objekte unterschiedlich ist
Chetan
11

hashCode() ::

Wenn Sie nur die Hash-Code-Methode überschreiben, passiert nichts. Weil es hashCodefür jedes Objekt als Objektklasse immer neu zurückgibt.

equals() ::

Wenn Sie nur die gleiche Methode überschreiben, a.equals(b)bedeutet dies, dass hashCodea und b gleich sein müssen, aber nicht passieren dürfen. Weil Sie die hashCodeMethode nicht überschrieben haben .

Hinweis: Die hashCode()Methode der hashCodeObjektklasse gibt für jedes Objekt immer neu zurück .

Wenn Sie also Ihr Objekt in der hashingbasierten Sammlung verwenden müssen, müssen Sie sowohl equals()und überschreiben hashCode().

Rinkal Gupta
quelle
Das ist ein interessanter Punkt, bei dem nur hashCode () überschrieben wird . Es ist völlig in Ordnung, oder? Oder kann es auch problematische Fälle geben?
Johnny
1
Dies ist eine irreführende und falsche Antwort. Durch Überschreiben von (= only =) hashCode () wird sichergestellt, dass jedes Objekt, das von der jeweiligen Klasse mit ähnlichen Eigenschaften instanziiert wird, denselben Hashcode trägt. Wird aber nicht nützlich sein, da keiner von ihnen gleich ist.
Mfaisalhyder
8

Java legt eine Regel fest, die

"Wenn zwei Objekte mit der Methode Object class equals gleich sind, sollte die Hashcode-Methode für diese beiden Objekte den gleichen Wert angeben."

Wenn wir also in unserer Klasse überschreiben equals(), sollten wir die hashcode()Methode auch überschreiben , um dieser Regel zu folgen. Beide Methoden equals()und hashcode()werden Hashtablebeispielsweise verwendet, um Werte als Schlüssel-Wert-Paare zu speichern. Wenn wir das eine und nicht das andere überschreiben, besteht die Möglichkeit, dass das Hashtablenicht wie gewünscht funktioniert, wenn wir ein solches Objekt als Schlüssel verwenden.

Ritesh Kaushik
quelle
6

Denn wenn Sie sie nicht überschreiben, verwenden Sie die Standardimplementierung in Object.

Angesichts der Tatsache, dass Instanzgleichheit und Hascode-Werte im Allgemeinen Kenntnisse darüber erfordern, was ein Objekt ausmacht, müssen sie im Allgemeinen in Ihrer Klasse neu definiert werden, um eine konkrete Bedeutung zu haben.

PaulJWilliams
quelle
6

Um unsere eigenen Klassenobjekte als Schlüssel in Sammlungen wie HashMap, Hashtable usw. zu verwenden, sollten wir beide Methoden (hashCode () und equals ()) überschreiben, indem wir uns mit der internen Funktionsweise der Sammlung vertraut machen. Andernfalls führt dies zu falschen Ergebnissen, die wir nicht erwarten.

Prashanth
quelle
6

Hinzufügen zu @Lombos Antwort

Wann müssen Sie equals () überschreiben?

Die Standardimplementierung von equals () von Object ist

public boolean equals(Object obj) {
        return (this == obj);
}

Dies bedeutet, dass zwei Objekte nur dann als gleich betrachtet werden, wenn sie dieselbe Speicheradresse haben. Dies gilt nur, wenn Sie ein Objekt mit sich selbst vergleichen.

Möglicherweise möchten Sie jedoch zwei Objekte als gleich betrachten, wenn sie für eine oder mehrere ihrer Eigenschaften denselben Wert haben (siehe das Beispiel in der Antwort von @Lombo).

Sie werden also equals()in diesen Situationen außer Kraft setzen und Ihre eigenen Bedingungen für die Gleichstellung angeben.

Ich habe equals () erfolgreich implementiert und es funktioniert hervorragend. Warum werden sie auch gebeten, hashCode () zu überschreiben?

Solange Sie keine "Hash" -basierten Sammlungen für Ihre benutzerdefinierte Klasse verwenden, ist dies in Ordnung. Aber irgendwann in der Zukunft möchten Sie vielleicht verwenden HashMapoder HashSetund wenn Sie hashCode () nicht overrideund "korrekt implementieren" , funktionieren diese Hash-basierten Sammlungen nicht wie beabsichtigt.

Überschreiben ist nur gleich (Ergänzung zu @Lombos Antwort)

myMap.put(first,someValue)
myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?

Zunächst prüft HashMap, ob der Hashcode von secondidentisch ist mit first. Nur wenn die Werte gleich sind, wird die Gleichheit im selben Bucket überprüft.

Hier unterscheidet sich der Hashcode jedoch für diese beiden Objekte (da sie eine andere Speicheradresse haben als die Standardimplementierung). Daher wird es nicht einmal wichtig sein, die Gleichheit zu überprüfen.

Wenn Ihre überschriebene equals () -Methode einen Haltepunkt enthält, wird dieser nicht aktiviert, wenn sie unterschiedliche Hashcodes haben. contains()prüft hashCode()und nur wenn sie gleich sind, würde es Ihre equals()Methode aufrufen .

Warum können wir die HashMap nicht in allen Buckets auf Gleichheit prüfen lassen? Ich muss also nicht hashCode () überschreiben !!

Dann fehlt Ihnen der Punkt der Hash-basierten Sammlungen. Folgendes berücksichtigen :

Your hashCode() implementation : intObject%9.

Im Folgenden sind die Schlüssel aufgeführt, die in Form von Eimern gespeichert sind.

Bucket 1 : 1,10,19,... (in thousands)
Bucket 2 : 2,20,29...
Bucket 3 : 3,21,30,...
...

Angenommen, Sie möchten wissen, ob die Karte den Schlüssel 10 enthält. Möchten Sie alle Eimer durchsuchen? oder Möchten Sie nur einen Eimer durchsuchen?

Basierend auf dem HashCode würden Sie identifizieren, dass wenn 10 vorhanden ist, es in Bucket 1 vorhanden sein muss. Es wird also nur Bucket 1 durchsucht !!

user104309
quelle
5
class A {
    int i;
    // Hashing Algorithm
    if even number return 0 else return 1
    // Equals Algorithm,
    if i = this.i return true else false
}
  • put ('key', 'value') berechnet den Hash-Wert hashCode()zur Bestimmung des Buckets und ermittelt mithilfe der equals()Methode, ob der Wert bereits im Bucket vorhanden ist. Wenn nicht, wird es hinzugefügt, andernfalls wird es durch den aktuellen Wert ersetzt
  • get ('key') wird verwendet hashCode(), um zuerst den Eintrag (Bucket) und equals()den Wert in Entry zu finden

wenn beide überschrieben werden,

Karte < A >

Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...

if equals wird nicht überschrieben

Karte < A >

Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..

Wenn hashCode nicht überschrieben wird

Karte < A >

Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...

HashCode Equal Contract

  1. Zwei Schlüssel, die nach der gleichen Methode gleich sind, sollten denselben Hashcode erzeugen
  2. Zwei Schlüssel, die denselben Hashcode erzeugen, müssen nicht gleich sein (im obigen Beispiel generieren alle geraden Zahlen denselben Hashcode).
Bharanitharan
quelle
4

Betrachten Sie die Sammlung von Bällen in einem Eimer in schwarzer Farbe. Ihre Aufgabe ist es, diese Kugeln wie folgt zu färben und für ein geeignetes Spiel zu verwenden.

Für Tennis - Gelb, Rot. Für Cricket - Weiß

Jetzt hat Eimer Kugeln in drei Farben Gelb, Rot und Weiß. Und das hast du jetzt gemacht. Nur du weißt, welche Farbe für welches Spiel ist.

Bälle färben - Hashing. Den Ball für das Spiel auswählen - Gleich.

Wenn Sie die Färbung gemacht haben und jemand den Ball entweder für Cricket oder Tennis wählt, stört ihn die Farbe nicht !!!

Bharanitharan
quelle
4

Ich habe mir die Erklärung angesehen: "Wenn Sie nur HashCode überschreiben, wird beim Aufrufen myMap.put(first,someValue)zuerst der HashCode berechnet, der HashCode berechnet und in einem bestimmten Bucket gespeichert. Wenn Sie ihn aufrufen myMap.put(first,someOtherValue), sollte er gemäß der Kartendokumentation zuerst durch Second ersetzt werden, da sie gleich sind." (gemäß unserer Definition). " ::

Ich denke, wenn wir myMapdas zweite Mal hinzufügen , sollte es das 'zweite' Objekt seinmyMap.put(second,someOtherValue)

Narinder
quelle
4

1) Der häufigste Fehler ist im folgenden Beispiel dargestellt.

public class Car {

    private String color;

    public Car(String color) {
        this.color = color;
    }

    public boolean equals(Object obj) {
        if(obj==null) return false;
        if (!(obj instanceof Car))
            return false;   
        if (obj == this)
            return true;
        return this.color.equals(((Car) obj).color);
    }

    public static void main(String[] args) {
        Car a1 = new Car("green");
        Car a2 = new Car("red");

        //hashMap stores Car type and its quantity
        HashMap<Car, Integer> m = new HashMap<Car, Integer>();
        m.put(a1, 10);
        m.put(a2, 20);
        System.out.println(m.get(new Car("green")));
    }
}

Das grüne Auto wurde nicht gefunden

2. Problem verursacht durch hashCode ()

Das Problem wird durch die nicht überschriebene Methode verursacht hashCode(). Der Vertrag zwischen equals()und hashCode()ist:

  1. Wenn zwei Objekte gleich sind, müssen sie denselben Hashcode haben.
  2. Wenn zwei Objekte denselben Hashcode haben, können sie gleich sein oder nicht.

    public int hashCode(){  
      return this.color.hashCode(); 
    }
Neeraj Gahlawat
quelle
4

Dies ist nützlich, wenn Sie Wertobjekte verwenden . Das Folgende ist ein Auszug aus dem Portland Pattern Repository :

Beispiele für Wertobjekte sind Zahlen, Datumsangaben, Gelder und Zeichenfolgen. In der Regel handelt es sich um kleine Objekte, die häufig verwendet werden. Ihre Identität basiert eher auf ihrem Zustand als auf ihrer Objektidentität. Auf diese Weise können Sie mehrere Kopien desselben konzeptuellen Wertobjekts haben.

Ich kann also mehrere Kopien eines Objekts haben, das das Datum des 16. Januar 1998 darstellt. Jede dieser Kopien ist einander gleich. Für ein kleines Objekt wie dieses ist es oft einfacher, neue zu erstellen und zu verschieben, als sich auf ein einzelnes Objekt zu verlassen, um das Datum darzustellen.

Ein Wertobjekt sollte in Java (oder = in Smalltalk) immer .equals () überschreiben. (Denken Sie daran, auch .hashCode () zu überschreiben.)

Ionuț G. Stan
quelle
3

Angenommen, Sie haben eine Klasse (A), die zwei andere (B) (C) aggregiert, und Sie müssen Instanzen von (A) in der Hashtabelle speichern. Die Standardimplementierung ermöglicht nur die Unterscheidung von Instanzen, nicht jedoch nach (B) und (C). Zwei Instanzen von A könnten also gleich sein, aber standardmäßig können Sie sie nicht korrekt vergleichen.

Dewfy
quelle
3

Die Methoden equals und hashcode werden in der Objektklasse definiert. Wenn die Methode equals true zurückgibt, geht das System standardmäßig weiter und überprüft den Wert des Hash-Codes. Wenn der Hash-Code der beiden Objekte ebenfalls nur identisch ist, werden die Objekte als gleich betrachtet. Wenn Sie also nur die Methode equals überschreiben, zeigt der vom System definierte Hashcode möglicherweise nicht an, dass die beiden Objekte gleich sind, obwohl die überschriebene Methode equals 2 Objekte als gleich anzeigt. Wir müssen also auch den Hash-Code überschreiben.

Aarti
quelle
Wenn die Methode equals true zurückgibt, muss der Hashcode nicht überprüft werden. Wenn zwei Objekte unterschiedliche Hashcodes haben, sollte man sie jedoch als unterschiedlich betrachten können, ohne gleich aufrufen zu müssen. Darüber hinaus impliziert das Wissen, dass keines der Dinge in einer Liste einen bestimmten Hash-Code hat, dass keines der Dinge in der Liste ein Objekt mit diesem Hash-Code übereinstimmen kann. Wenn man beispielsweise eine Liste von Objekten hat, deren Hash-Codes gerade Zahlen sind, und eine Liste von Objekten, bei denen es sich um ungerade Zahlen handelt, befindet sich kein Objekt in der zweiten Liste, dessen Hash-Code eine gerade Zahl ist.
Supercat
Wenn man zwei Objekte X und Y hatte, deren "gleiche" Methoden angaben, dass sie übereinstimmten, aber der Hash-Code von X eine gerade Zahl und der Hash-Code von Y eine ungerade Zahl war, eine Sammlung wie oben beschrieben, die feststellte, dass der Hash-Code von Objekt Y ungerade und gespeichert war es in der zweiten Liste würde keine Übereinstimmung für Objekt X finden können. Es würde feststellen, dass der Hash-Code von X gerade war, und da die zweite Liste keine Objekte mit geradzahligen Hash-Codes enthält, würde es nicht stören um dort nach etwas zu suchen, das zu X passt, obwohl Y zu X passen würde. Was Sie sagen sollten ...
Supercat
... wäre, dass viele Sammlungen es vermeiden würden, Dinge zu vergleichen, deren Hash-Codes bedeuten würden, dass sie nicht gleich sein können. Bei zwei Objekten, deren Hash-Codes unbekannt sind, ist es oft schneller, sie direkt zu vergleichen, als ihre Hash-Codes zu berechnen. Daher gibt es keine Garantie dafür, dass Dinge, die ungleiche Hash-Codes melden, aber truefür zurückgeben equals, nicht als übereinstimmend angesehen werden. Wenn Sammlungen jedoch feststellen, dass Dinge nicht denselben Hash-Code haben können, bemerken sie wahrscheinlich nicht, dass sie gleich sind.
Supercat
3

Gleichheits- und Hashcode-Methoden in Java

Dies sind Methoden der Klasse java.lang.Object, die die Superklasse aller Klassen ist (auch benutzerdefinierte Klassen und andere in der Java-API definierte).

Implementierung:

public boolean equals (Object obj)

public int hashCode ()

Geben Sie hier die Bildbeschreibung ein

public boolean equals (Object obj)

Diese Methode prüft einfach, ob zwei Objektreferenzen x und y auf dasselbe Objekt verweisen. dh es wird geprüft, ob x == y ist.

Es ist reflexiv: Für jeden Referenzwert x sollte x.equals (x) true zurückgeben.

Es ist symmetrisch: Für alle Referenzwerte x und y sollte x.equals (y) genau dann true zurückgeben, wenn y.equals (x) true zurückgibt.

Es ist transitiv: Wenn für alle Referenzwerte x, y und z x.equals (y) true und y.equals (z) true zurückgibt, sollte x.equals (z) true zurückgeben.

Es ist konsistent: Für alle Referenzwerte x und y geben mehrere Aufrufe von x.equals (y) konsistent true oder konsistent false zurück, sofern keine Informationen geändert werden, die für gleiche Vergleiche für das Objekt verwendet werden.

Für jeden Nicht-Null-Referenzwert x sollte x.equals (null) false zurückgeben.

public int hashCode ()

Diese Methode gibt den Hashcode-Wert für das Objekt zurück, für das diese Methode aufgerufen wird. Diese Methode gibt den Hash-Code-Wert als Ganzzahl zurück und wird zugunsten von Hashing-basierten Auflistungsklassen wie Hashtable, HashMap, HashSet usw. unterstützt. Diese Methode muss in jeder Klasse überschrieben werden, die die Methode equals überschreibt.

Der allgemeine Vertrag von hashCode lautet:

Immer wenn es während einer Ausführung einer Java-Anwendung mehr als einmal für dasselbe Objekt aufgerufen wird, muss die hashCode-Methode konsistent dieselbe Ganzzahl zurückgeben, sofern keine Informationen geändert werden, die für gleiche Vergleiche für das Objekt verwendet werden.

Diese Ganzzahl muss von einer Ausführung einer Anwendung zu einer anderen Ausführung derselben Anwendung nicht konsistent bleiben.

Wenn zwei Objekte gemäß der Methode equals (Object) gleich sind, muss der Aufruf der hashCode-Methode für jedes der beiden Objekte dasselbe ganzzahlige Ergebnis liefern.

Es ist nicht erforderlich, dass der Aufruf der hashCode-Methode für jedes der beiden Objekte unterschiedliche ganzzahlige Ergebnisse liefern muss, wenn zwei Objekte gemäß der Methode equals (java.lang.Object) ungleich sind. Der Programmierer sollte sich jedoch bewusst sein, dass das Erzeugen unterschiedlicher ganzzahliger Ergebnisse für ungleiche Objekte die Leistung von Hashtabellen verbessern kann.

Gleiche Objekte müssen denselben Hash-Code erzeugen, solange sie gleich sind. Ungleiche Objekte müssen jedoch keine unterschiedlichen Hash-Codes erzeugen.

Ressourcen:

JavaRanch

Bild

Affy
quelle
Das Bild (Videolink) befindet sich im privaten Modus. Machen Sie es öffentlich zu sehen.
UdayKiran Pulipati
2

Wenn Sie im folgenden Beispiel die Überschreibung für Gleichheit oder Hashcode in der Person-Klasse auskommentieren, kann dieser Code Toms Reihenfolge nicht nachschlagen. Die Verwendung der Standardimplementierung von Hashcode kann zu Fehlern bei der Suche nach Hashtabellen führen.

Was ich unten habe, ist ein vereinfachter Code, der die Reihenfolge der Personen nach Person aufruft. Die Person wird als Schlüssel in der Hashtabelle verwendet.

public class Person {
    String name;
    int age;
    String socialSecurityNumber;

    public Person(String name, int age, String socialSecurityNumber) {
        this.name = name;
        this.age = age;
        this.socialSecurityNumber = socialSecurityNumber;
    }

    @Override
    public boolean equals(Object p) {
        //Person is same if social security number is same

        if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
            return true;
        } else {
            return false;
        }

    }

    @Override
    public int hashCode() {        //I am using a hashing function in String.java instead of writing my own.
        return socialSecurityNumber.hashCode();
    }
}


public class Order {
    String[]  items;

    public void insertOrder(String[]  items)
    {
        this.items=items;
    }

}



import java.util.Hashtable;

public class Main {

    public static void main(String[] args) {

       Person p1=new Person("Tom",32,"548-56-4412");
        Person p2=new Person("Jerry",60,"456-74-4125");
        Person p3=new Person("Sherry",38,"418-55-1235");

        Order order1=new Order();
        order1.insertOrder(new String[]{"mouse","car charger"});

        Order order2=new Order();
        order2.insertOrder(new String[]{"Multi vitamin"});

        Order order3=new Order();
        order3.insertOrder(new String[]{"handbag", "iPod"});

        Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
        hashtable.put(p1,order1);
        hashtable.put(p2,order2);
        hashtable.put(p3,order3);

       //The line below will fail if Person class does not override hashCode()
       Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
        for(String item:tomOrder.items)
        {
            System.out.println(item);
        }
    }
}
Entwickler747
quelle
2

String-Klassen- und Wrapper-Klassen haben eine andere Implementierung equals()und hashCode()Methoden als die Object-Klasse. Die Methode equals () der Objektklasse vergleicht die Referenzen der Objekte und nicht den Inhalt. Die Methode hashCode () der Object-Klasse gibt für jedes einzelne Objekt einen eigenen Hashcode zurück, unabhängig davon, ob der Inhalt identisch ist.

Dies führt zu Problemen, wenn Sie die Map-Sammlung verwenden und der Schlüssel vom Typ Persistent, vom Typ StringBuffer / Builder ist. Da sie im Gegensatz zur String-Klasse equals () und hashCode () nicht überschreiben, gibt equals () false zurück, wenn Sie zwei verschiedene Objekte vergleichen, obwohl beide denselben Inhalt haben. Dadurch wird die HashMap dazu gebracht, dieselben Inhaltsschlüssel zu speichern. Das Speichern derselben Inhaltsschlüssel bedeutet, dass die Map-Regel verletzt wird, da Map überhaupt keine doppelten Schlüssel zulässt. Daher überschreiben Sie die Methoden equals () sowie hashCode () in Ihrer Klasse und stellen die Implementierung bereit (IDE kann diese Methoden generieren), sodass sie genauso funktionieren wie String's equals () und hashCode () und dieselben Inhaltsschlüssel verhindern.

Sie müssen die hashCode () -Methode zusammen mit equals () überschreiben, da equals () gemäß dem Hashcode funktioniert.

Darüber hinaus hilft das Überschreiben der hashCode () -Methode zusammen mit equals (), den Vertrag equals () - hashCode () zu intaktieren: "Wenn zwei Objekte gleich sind, müssen sie denselben Hashcode haben."

Wann müssen Sie eine benutzerdefinierte Implementierung für hashCode () schreiben?

Wie wir wissen, basiert die interne Arbeitsweise von HashMap auf dem Prinzip des Hashing. Es gibt bestimmte Buckets, in denen Eintragssätze gespeichert werden. Sie passen die Implementierung von hashCode () an Ihre Anforderungen an, sodass Objekte derselben Kategorie in demselben Index gespeichert werden können. Wenn Sie die Werte mithilfe der put(k,v)Methode in der Map-Sammlung speichern , lautet die interne Implementierung von put ():

put(k, v){
hash(k);
index=hash & (n-1);
}

Das heißt, es wird ein Index generiert und der Index wird basierend auf dem Hashcode eines bestimmten Schlüsselobjekts generiert. Lassen Sie diese Methode also Hashcode gemäß Ihren Anforderungen generieren, da dieselben Hashcode-Eintragssätze in demselben Bucket oder Index gespeichert werden.

Das ist es!

Arun Raaj
quelle
1

hashCode()Methode wird verwendet, um eine eindeutige Ganzzahl für ein bestimmtes Objekt zu erhalten. Diese ganze Zahl ist , für die Bestimmung der Lage Eimer verwendet, wenn dieses Objekt in einigen gespeichert werden muss HashTable, HashMapwie Datenstruktur. Standardmäßig gibt die hashCode()Methode von Object eine ganzzahlige Darstellung der Speicheradresse zurück, in der das Objekt gespeichert ist.

Die hashCode()Methode von Objekten wird verwendet, wenn wir sie in ein HashTable, HashMapoder einfügen HashSet. Mehr dazu HashTablesauf Wikipedia.org als Referenz.

Um einen Eintrag in die Kartendatenstruktur einzufügen, benötigen wir sowohl Schlüssel als auch Wert. Wenn sowohl Schlüssel als auch Werte benutzerdefinierte Datentypen sind, bestimmt hashCode()der Schlüssel, wo das Objekt intern gespeichert werden soll. Wenn Sie das Objekt auch von der Karte aus nachschlagen müssen, bestimmt der Hash-Code des Schlüssels, wo nach dem Objekt gesucht werden soll.

Der Hash-Code zeigt intern nur auf einen bestimmten "Bereich" (oder eine Liste, einen Bucket usw.). Da verschiedene Schlüsselobjekte möglicherweise denselben Hashcode haben können, ist der Hashcode selbst keine Garantie dafür, dass der richtige Schlüssel gefunden wird. Das HashTableiteriert dann diesen Bereich (alle Schlüssel mit demselben Hashcode) und verwendet die Schlüsselmethode equals(), um den richtigen Schlüssel zu finden. Sobald der richtige Schlüssel gefunden wurde, wird das für diesen Schlüssel gespeicherte Objekt zurückgegeben.

Wie wir sehen können, wird beim Speichern und Nachschlagen von Objekten in a eine Kombination der Methoden hashCode()und equals()verwendet HashTable.

ANMERKUNGEN:

  1. Verwenden Sie zum Generieren immer dieselben Attribute eines Objekts hashCode()und equals()beides. Wie in unserem Fall haben wir die Mitarbeiter-ID verwendet.

  2. equals() muss konsistent sein (wenn die Objekte nicht geändert werden, muss immer derselbe Wert zurückgegeben werden).

  3. Wann immer a.equals(b), dann a.hashCode()muss das gleiche sein wie b.hashCode().

  4. Wenn Sie einen überschreiben, sollten Sie den anderen überschreiben.

http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html

Paramesh Korrakuti
quelle
hashCode()wird nicht verwendet, um für jedes Objekt eine eindeutige Ganzzahl zurückzugeben. Das ist unmöglich. Sie haben dies selbst im zweiten Satz des vierten Absatzes widersprochen.
Marquis von Lorne
@EJP, meistens gibt hascode () die eindeutige Interger für zwei verschiedene Objekte zurück. Es besteht jedoch die Möglichkeit, dass Hascode für zwei verschiedene Objekte kollidiert. Dieses Konzept wird als Hashcode-Kollision bezeichnet . Bitte beziehen Sie sich auf: tech.queryhome.com/96931/…
Paramesh Korrakuti
1

IMHO heißt es in der Regel: Wenn zwei Objekte gleich sind, sollten sie denselben Hash haben, dh gleiche Objekte sollten gleiche Hashwerte erzeugen.

Wie oben angegeben, ist default equals () in Object ==, was einen Vergleich der Adresse durchführt. HashCode () gibt die Adresse in einer Ganzzahl (Hash der tatsächlichen Adresse) zurück, die wiederum für ein bestimmtes Objekt unterschiedlich ist.

Wenn Sie die benutzerdefinierten Objekte in den Hash-basierten Sammlungen verwenden müssen, müssen Sie sowohl equals () als auch hashCode () überschreiben. Beispiel Wenn ich das HashSet der Mitarbeiterobjekte beibehalten möchte, wenn ich keinen stärkeren hashCode und equals verwende Möglicherweise überschreibe ich die beiden verschiedenen Mitarbeiterobjekte. Dies passiert, wenn ich das Alter als hashCode () verwende. Ich sollte jedoch den eindeutigen Wert verwenden, der die Mitarbeiter-ID sein kann.

Cleonjoys
quelle
1

Damit Sie nach doppelten Objekten suchen können, benötigen wir einen benutzerdefinierten Gleichheits- und Hashcode.

Da Hashcode immer eine Zahl zurückgibt, ist es immer schnell, ein Objekt mit einer Zahl anstelle eines alphabetischen Schlüssels abzurufen. Wie wird es gehen? Angenommen, wir haben ein neues Objekt erstellt, indem wir einen Wert übergeben haben, der bereits in einem anderen Objekt verfügbar ist. Jetzt gibt das neue Objekt den gleichen Hashwert wie ein anderes Objekt zurück, da der übergebene Wert der gleiche ist. Sobald derselbe Hashwert zurückgegeben wird, wechselt JVM jedes Mal zur gleichen Speicheradresse. Wenn mehr als ein Objekt für denselben Hashwert vorhanden ist, wird die Methode equals () verwendet, um das richtige Objekt zu identifizieren.

Tavash
quelle
1

Wenn Sie Ihr benutzerdefiniertes Objekt als Schlüssel in Map speichern und abrufen möchten, sollten Sie immer equals und hashCode in Ihrem benutzerdefinierten Objekt überschreiben. Z.B:

Person p1 = new Person("A",23);
Person p2 = new Person("A",23);
HashMap map = new HashMap();
map.put(p1,"value 1");
map.put(p2,"value 2");

Hier wird p1 & p2 als nur ein Objekt betrachtet und die mapGröße wird nur 1 sein, weil sie gleich sind.

Ambrish Rajput
quelle
1
public class Employee {

    private int empId;
    private String empName;

    public Employee(int empId, String empName) {
        super();
        this.empId = empId;
        this.empName = empName;
    }

    public int getEmpId() {
        return empId;
    }

    public void setEmpId(int empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    @Override
    public String toString() {
        return "Employee [empId=" + empId + ", empName=" + empName + "]";
    }

    @Override
    public int hashCode() {
        return empId + empName.hashCode();
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }
        if (!(this instanceof Employee)) {
            return false;
        }
        Employee emp = (Employee) obj;
        return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName());
    }

}

Testklasse

public class Test {

    public static void main(String[] args) {
        Employee emp1 = new Employee(101,"Manash");
        Employee emp2 = new Employee(101,"Manash");
        Employee emp3 = new Employee(103,"Ranjan");
        System.out.println(emp1.hashCode());
        System.out.println(emp2.hashCode());
        System.out.println(emp1.equals(emp2));
        System.out.println(emp1.equals(emp3));
    }

}

In Object Class wird equals (Object obj) verwendet, um den Adressvergleich zu vergleichen. Wenn Sie in der Testklasse zwei Objekte vergleichen, entspricht dies der Methode false, aber wenn wir Hashcode () überschreiben, kann der Inhalt verglichen und das richtige Ergebnis erzielt werden.

Manash Ranjan Dakua
quelle
und Testklasse, die ich im folgenden Programm hinzugefügt habe.
Manash Ranjan Dakua
In Object Class wird equals (Object obj) verwendet, um den Adressvergleich zu vergleichen. Wenn Sie in der Testklasse zwei Objekte vergleichen, entspricht dies der Methode false, aber wenn wir Hashcode () überschreiben, kann der Inhalt verglichen und das richtige Ergebnis erzielt werden.
Manash Ranjan Dakua
1
Sie können den Bearbeitungslink direkt unter dieser Antwort verwenden, um Ihre Antwort zu ergänzen. Bitte fügen Sie keine Antwort als zwei unvollständige hinzu
Suraj Rao
1

Wenn Sie überschreiben equals()und nicht hashcode(), werden Sie kein Problem finden, es sei denn, Sie oder jemand anderes verwendet diesen Klassentyp in einer Hash-Sammlung wie HashSet. Die Leute vor mir haben die dokumentierte Theorie mehrmals klar erklärt. Ich bin nur hier, um ein sehr einfaches Beispiel zu liefern.

Stellen Sie sich eine Klasse vor, deren equals()Bedürfnis etwas Angepasstes bedeuten muss:

    public class Rishav {

        private String rshv;

        public Rishav(String rshv) {
            this.rshv = rshv;
        }

        /**
        * @return the rshv
        */
        public String getRshv() {
            return rshv;
        }

        /**
        * @param rshv the rshv to set
        */
        public void setRshv(String rshv) {
            this.rshv = rshv;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Rishav) {
                obj = (Rishav) obj;
                if (this.rshv.equals(((Rishav) obj).getRshv())) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return rshv.hashCode();
        }

    }

Betrachten Sie nun diese Hauptklasse: -

    import java.util.HashSet;
    import java.util.Set;

    public class TestRishav {

        public static void main(String[] args) {
            Rishav rA = new Rishav("rishav");
            Rishav rB = new Rishav("rishav");
            System.out.println(rA.equals(rB));
            System.out.println("-----------------------------------");

            Set<Rishav> hashed = new HashSet<>();
            hashed.add(rA);
            System.out.println(hashed.contains(rB));
            System.out.println("-----------------------------------");

            hashed.add(rB);
            System.out.println(hashed.size());
        }

    }

Dies ergibt die folgende Ausgabe: -

    true
    -----------------------------------
    true
    -----------------------------------
    1

Ich bin mit den Ergebnissen zufrieden. Aber wenn ich nicht überschrieben habe hashCode(), wird es einen Albtraum verursachen, da Objekte Rishavmit demselben Mitgliedsinhalt nicht mehr als eindeutig behandelt hashCodewerden, da sie unterschiedlich sind, wie durch das Standardverhalten erzeugt. Hier wäre die Ausgabe: -

    true
    -----------------------------------
    false
    -----------------------------------
    2
91StarSky
quelle
0

Beide Methoden sind in der Objektklasse definiert. Und beide sind in ihrer einfachsten Implementierung. Wenn Sie diesen Methoden also eine weitere Implementierung hinzufügen möchten, müssen Sie sie in Ihrer Klasse überschreiben.

Zum Beispiel überprüft die Methode equals () im Objekt nur ihre Gleichheit in der Referenz. Wenn Sie also auch den Status vergleichen müssen, können Sie diesen überschreiben, wie dies in der String-Klasse der Fall ist.

GuruKulki
quelle
-3

Bah - "Sie müssen hashCode () in jeder Klasse überschreiben, die equals () überschreibt."

[aus Effective Java von Joshua Bloch?]

Ist das nicht falsch herum? Das Überschreiben von hashCode impliziert wahrscheinlich, dass Sie eine Hash-Key-Klasse schreiben, das Überschreiben von equals jedoch sicherlich nicht. Es gibt viele Klassen, die nicht als Hash-Schlüssel verwendet werden, aber aus einem anderen Grund eine Methode zum Testen der logischen Gleichheit wünschen. Wenn Sie dafür "gleich" wählen, können Sie durch übereifrige Anwendung dieser Regel aufgefordert werden, eine HashCode-Implementierung zu schreiben. Alles, was erreicht wird, ist das Hinzufügen von ungetestetem Code in die Codebasis, ein Übel, das darauf wartet, in Zukunft jemanden zu stolpern. Auch das Schreiben von Code, den Sie nicht benötigen, ist nicht agil. Es ist einfach falsch (und eine von einer Idee generierte wird wahrscheinlich nicht mit Ihren handgefertigten Gleichen kompatibel sein).

Sicherlich hätten sie eine Schnittstelle für Objekte vorschreiben sollen, die als Schlüssel verwendet werden sollen? Unabhängig davon sollte Object niemals standardmäßig hashCode () und equals () imho angegeben haben. Es wird wahrscheinlich viele kaputte Hash-Sammlungen ermutigt.

Trotzdem denke ich, dass die "Regel" von hinten nach vorne geschrieben ist. In der Zwischenzeit werde ich weiterhin vermeiden, "Gleichheit" für Gleichheitstestmethoden zu verwenden :-(

Alan Dobbie
quelle