Dlaczego muszę przesłonić metody equals i hashCode w Javie?

383

Niedawno przeczytałem ten dokument dotyczący prac programistycznych .

Dokument jest o definiowaniu hashCode()i equals()skutecznie i poprawnie, jednak nie jestem w stanie zrozumieć, dlaczego musimy zastąpić te dwie metody.

Jak mogę podjąć decyzję o wydajnym wdrożeniu tych metod?

Shashi
źródło
4
Istnieją dwa świetne artykuły na temat programowania. Poradnik wyjaśniający dokładnie to: Kiedy należy zastąpić równość? i dlaczego zawsze należy zastępować hashCode, gdy zastępowanie jest równe . (Ostrzeżenie, zaakceptowana odpowiedź jest w rzeczywistości błędna.)
aioobe,
Zastąpienie wielkości liter równa się: dwa takie same obiekty będą miały inny kod skrótu = te same obiekty będą się znajdować w innym segmencie (duplikacja). Case Override only hashcode: dwa takie same obiekty będą miały ten sam hashcode = ten sam obiekt przejdzie do tego samego segmentu (duplikacja).
VdeX

Odpowiedzi:

524

Joshua Bloch mówi o Effective Java

Musisz zastąpić hashCode () w każdej klasie, która przesłania equals (). Niezastosowanie się do tego spowoduje naruszenie ogólnej umowy dotyczącej Object.hashCode (), co uniemożliwi prawidłowe działanie Twojej klasy w połączeniu ze wszystkimi kolekcjami opartymi na hash, w tym HashMap, HashSet i Hashtable.

Spróbujmy to zrozumieć na przykładzie tego, co by się stało, gdybyśmy przesłonili equals()bez przesłonięcia hashCode()i spróbowali użyć Map.

Że mamy klasę i tak, że dwa obiekty MyClasssą równe, jeśli ich importantFieldjest równy (z hashCode()a equals()generowany przez Eclipse)

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;
    }

}

Zastąp tylko equals

Jeśli tylko equalsjest nadpisane, to kiedy zadzwonisz myMap.put(first,someValue)pierwszy, będzie mieszał się z jakimś wiadrem, a kiedy zadzwonisz myMap.put(second,someOtherValue), będzie mieszał się z innym wiadrem (ponieważ mają inny hashCode). Tak więc, mimo że są równe, ponieważ nie mieszają do tego samego segmentu, mapa nie może tego zrealizować i oba pozostają na mapie.


Chociaż nie jest to konieczne, aby zastąpić equals()jeśli przesłonić hashCode(), zobaczmy, co się stanie w tym szczególnym przypadku, gdy wiemy, że dwa obiekty MyClasssą równe, jeśli ich importantFieldjest równa, ale nie zastępują equals().

Zastąp tylko hashCode

Wyobraź sobie, że masz to

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

Jeśli przesłonisz tylko hashCodewtedy, gdy zadzwonisz myMap.put(first,someValue), najpierw bierze, oblicza ją hashCodei zapisuje w danym segmencie. Następnie, kiedy zadzwonisz myMap.put(second,someOtherValue), należy najpierw zamienić go na drugi zgodnie z Dokumentacją mapy, ponieważ są one równe (zgodnie z wymaganiami biznesowymi).

Problem polega jednak na tym, że równość nie została przedefiniowana, więc kiedy mapa haszy się secondi iteruje przez segment, sprawdzając, czy istnieje taki obiekt k, który second.equals(k)jest prawdziwy, nie znajdzie go tak second.equals(first), jak będzie false.

Mam nadzieję, że to było jasne

Lombo
źródło
5
czy możesz bardziej szczegółowo opracować, w drugim przypadku, dlaczego drugi obiekt musi przejść do innego wiadra?
Hussain Akhtar Wahid „Ghouri”
57
Nie podoba mi się ta odpowiedź, ponieważ sugeruje, że nie można przesłonić hashCode () bez przesłaniania equals (), co jest po prostu nieprawdą. Mówisz, że twój przykładowy kod (część „zastępuj tylko hashCode”) nie będzie działał, ponieważ definiujesz dwa obiekty jako równe, ale - przepraszam - ta definicja jest tylko w twojej głowie. W pierwszym przykładzie masz dwa nierównomierne obiekty z tym samym hashCode, i jest to całkowicie legalne. Powodem, dla którego musisz zastąpić equals (), nie jest to, że już przesłoniłeś hashCode (), ale dlatego, że chcesz przenieść definicję „equals” z głowy do kodu.
user2543253,
11
if you think you need to override one, then you need to override both of themjest źle. Musisz przesłonić, hashCodejeśli twoja klasa zastępuje, equalsale odwrócenie nie jest prawdą.
akhil_mittal
4
Myślę, że całkowicie poprawne jest zastąpienie tylko hashCode () bez przesłonięcia równości (). Jest też napisane w Effective Java : books.google.fr/…
Johnny
2
@ PhantomReference, zauważ, że tylko zastąpienie equalsnaruszyłoby umowę zapisaną w javadoc Object: „Jeśli dwa obiekty są równe zgodnie z equals(Object)metodą, wówczas wywołanie hashCodemetody na każdym z dwóch obiektów musi dać ten sam wynik liczb całkowitych”. Jasne, nie wszystkie części wszystkich umów są wykonywane w całym kodzie, ale formalnie rzecz biorąc, jest to naruszenie i uważam, że to błąd, który czeka.
aioobe
263

Kolekcje takie jak HashMapi HashSetużywają wartości hashcode obiektu w celu ustalenia, jak powinien być przechowywany w kolekcji, a hashcode jest ponownie wykorzystywany w celu zlokalizowania obiektu w jego kolekcji.

Pobieranie mieszania jest procesem dwuetapowym:

  1. Znajdź odpowiednie wiadro (za pomocą hashCode())
  2. Wyszukaj w wiadrze odpowiedni element (za pomocą equals())

Oto mały przykład, dlaczego powinniśmy pominąć equals()i hashcode().

Rozważ Employeeklasę, która ma dwa pola: wiek i imię.

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;
        }
     */
}

Teraz utwórz klasę, wstaw Employeeobiekt do a HashSeti sprawdź, czy ten obiekt jest obecny, czy nie.

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());
    }
}

Wydrukuje następujące:

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

Teraz odkomentuj hashcode()metodę, wykonaj to samo, a wynikiem będzie:

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

Czy widzisz teraz, dlaczego jeśli dwa obiekty są uważane za równe, ich kody skrótu również muszą być równe? W przeciwnym razie nigdy nie będziesz w stanie znaleźć obiektu od wartości domyślnej metoda hashcode w klasie Object praktycznie zawsze podaje unikalny numer dla każdego obiektu, nawet jeśli equals()metoda zostanie zastąpiona w taki sposób, że dwa lub więcej obiektów uważa się za równe . Nie ma znaczenia, jak równe są obiekty, jeśli ich kody skrótu tego nie odzwierciedlają. A więc jeszcze raz: jeśli dwa obiekty są równe, ich kody skrótu również muszą być równe.

rajeev pani ..
źródło
4
Idealny przykład. Wyraźnie pokazał różnicę!
coderpc
3
miło wyjaśniono @rajeev
VdeX
2
@VikasVerma równa się obiekt będzie miał jednakowy hashcode nie oznacza, że ​​nierówny obiekt będzie miał nierówny hashcode. Co jeśli obiekty są różne, ale ich kod skrótu jest taki sam?
Ravi,
1
wyjaśnił bardzo ładnie :)
Rahul
4
znacznie lepsza odpowiedź niż zaakceptowana odpowiedź! Dzięki
driftwood
50

Musisz zastąpić hashCode () w każdej klasie, która przesłania equals (). Niezastosowanie się do tego spowoduje naruszenie ogólnej umowy dotyczącej Object.hashCode (), co uniemożliwi prawidłowe działanie Twojej klasy w połączeniu ze wszystkimi kolekcjami opartymi na hash, w tym HashMap, HashSet i Hashtable.


   z Effective Java autorstwa Joshua Bloch

Definiując equals()i hashCode()konsekwentnie, możesz poprawić użyteczność swoich klas jako kluczy w kolekcjach opartych na haszowaniu. Jak wyjaśnia dokument API dla hashCode: „Ta metoda jest obsługiwana na korzyść tabel hasht, takich jak te dostarczane przez java.util.Hashtable”.

Najlepsza odpowiedź na pytanie dotyczące efektywnego wdrożenia tych metod sugeruje przeczytanie Rozdziału 3 Skutecznej Javy .

JuanZe
źródło
4
To poprawna odpowiedź. Następstwem jest oczywiście to, że jeśli nigdy nie użyjesz klasy w kolekcji opartej na haszowaniu, nie ma znaczenia, że ​​nie zaimplementowałeś hashCode().
szczupły
1
W bardziej skomplikowanych przypadkach nigdy nie wiadomo, czy używane kolekcje używają skrótów, więc trzymaj się z daleka od „nie ma znaczenia, że ​​nie wdrożyłeś hashCode ()”
Victor Sergienko
1
Czy mogę zastąpić hashCode () bez przesłaniania equals ()?
Johnny
@StasS, tak, wbrew temu, co mówi zaakceptowana odpowiedź. Zobacz wyjaśnienie w drugiej części tego artykułu: Dlaczego zawsze należy zastępować hashCode, gdy zastępowanie jest równe
aioobe
22

Mówiąc najprościej, metoda equals w Object Check dla równości referencyjnej, gdzie jako dwie instancje twojej klasy mogą być semantycznie równe, gdy właściwości są równe. Jest to na przykład ważne, gdy umieszczasz swoje obiekty w kontenerze, który używa znaku równości i kodu skrótu, takiego jak HashMap i Set . Powiedzmy, że mamy taką klasę jak:

public class Foo {
    String id;
    String whatevs;

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

Tworzymy dwa wystąpienia o tym samym identyfikatorze :

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

Bez nadpisywania równych otrzymujemy:

  • a.equals (b) jest fałszem, ponieważ są to dwa różne wystąpienia
  • a.equals (a) jest prawdą, ponieważ jest to ta sama instancja
  • b.equals (b) jest prawdą, ponieważ jest to ta sama instancja

Poprawny? Cóż, może jeśli tego właśnie chcesz. Powiedzmy, że chcemy, aby obiekty o tym samym identyfikatorze były tym samym obiektem, niezależnie od tego, czy są to dwa różne wystąpienia. Zastępujemy równe (i 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();
    }
}

Jeśli chodzi o implementację równości i kodu skrótu, mogę polecić użycie metod pomocniczych Guavy

crunchdog
źródło
20

Tożsamość nie jest równością.

  • równa się ==tożsamość testu operatora .
  • equals(Object obj) metoda porównuje test równości (tzn. musimy powiedzieć równość przez przesłonięcie metody)

Dlaczego muszę przesłonić metody equals i hashCode w Javie?

Najpierw musimy zrozumieć zastosowanie metody równości.

Aby odróżnić różnice między dwoma obiektami, musimy zastąpić metodę równości.

Na przykład:

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

Teraz metoda hashCode może łatwo zrozumieć.

hashCode tworzy liczbę całkowitą w celu przechowywania obiektu w strukturach danych takich jak HashMap , HashSet .

Załóżmy, że mamy metodę zastępowania równą Customerjak powyżej,

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

Podczas pracy ze strukturą danych, gdy przechowujemy obiekt w wiadrach (wiadro to fantazyjna nazwa folderu). Jeśli użyjemy wbudowanej techniki skrótu, dla dwóch klientów generuje ona dwa różne kody skrótu. Dlatego przechowujemy ten sam identyczny obiekt w dwóch różnych miejscach. Aby uniknąć tego rodzaju problemów, powinniśmy przesłonić metodę hashCode również w oparciu o następujące zasady.

  • nierówne wystąpienia mogą mieć ten sam kod skrótu.
  • równe instancje powinny zwrócić ten sam kod skrótu.
Premraj
źródło
3
Tego szukałem od ostatniej godziny. Awesome mate (y)
Adnan
13

Ok, pozwól mi wyjaśnić pojęcie w bardzo prostych słowach.

Po pierwsze, z szerszej perspektywy, mamy kolekcje, a hashapa jest jedną z struktur danych w kolekcjach.

Aby zrozumieć, dlaczego musimy zastąpić zarówno metodę equals, jak i hashcode, w razie potrzeby najpierw zrozumiemy, co jest hashap i co robi.

Hashap jest strukturą danych, która przechowuje pary kluczowych wartości danych w sposób tablicowy. Powiedzmy, że [], gdzie każdy element w „a” jest parą klucz-wartość.

Również każdy indeks w powyższej tablicy może być połączoną listą, dzięki czemu ma więcej niż jedną wartość w jednym indeksie.

Dlaczego teraz używana jest mapa? Jeśli musimy przeszukiwać dużą tablicę, to przeszukujemy każdą z nich, jeśli nie będą one wydajne, więc jaka technika haszowania mówi nam, że pozwala wstępnie przetworzyć tablicę z pewną logiką i pogrupować elementy w oparciu o tę logikę, tj. Mieszanie

np .: mamy tablicę 1,2,3,4,5,6,7,8,9,10,11 i stosujemy mod funkcji skrótu 10, więc 1,11 zostanie zgrupowane razem. Więc jeśli musielibyśmy szukać 11 w poprzedniej tablicy, musielibyśmy iterować całą tablicę, ale kiedy ją pogrupujemy, ograniczamy nasz zakres iteracji, zwiększając w ten sposób szybkość. Ta struktura danych używana do przechowywania wszystkich powyższych informacji może być traktowana jako tablica 2D dla uproszczenia

Teraz oprócz powyższego hashapa mówi również, że nie doda w nim żadnych duplikatów. I to jest główny powód, dla którego musimy zastąpić equals i hashcode

Więc kiedy powiedziano, że wyjaśnia wewnętrzne działanie haszapa, musimy znaleźć metody, które ma haszapa i jak postępuje zgodnie z powyższymi zasadami, które wyjaśniłem powyżej

więc w haszapie jest metoda o nazwie as (K, V) i zgodnie z haszapem powinna ona spełniać powyższe zasady efektywnego rozmieszczania tablicy i nie dodawania żadnych duplikatów

więc to, co robi, polega na tym, że najpierw wygeneruje kod skrótu dla danego klucza, aby zdecydować, w którym indeksie powinna wejść wartość. jeśli nic nie ma w tym indeksie, wówczas nowa wartość zostanie tam dodana, jeśli coś już tam jest następnie nową wartość należy dodać po zakończeniu listy połączonej pod tym indeksem. ale pamiętaj, że nie należy dodawać duplikatów zgodnie z pożądanym zachowaniem mapy skrótów. powiedzmy, że masz dwa obiekty całkowite aa = 11, bb = 11. jak każdy obiekt wyprowadzony z klasy obiektu, domyślną implementacją do porównywania dwóch obiektów jest porównanie referencji, a nie wartości wewnątrz obiektu. Tak więc w powyższym przypadku oba, choć semantycznie równe, nie przejdą testu równości, i istnieje możliwość, że dwa obiekty o tym samym haszu i takich samych wartościach będą istnieć, tworząc w ten sposób duplikaty. Jeśli zastąpimy, moglibyśmy uniknąć dodawania duplikatów. Możesz również odnieść się doSzczegóły pracy

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
źródło
Mam jedno zamieszanie, dlaczego musimy przesłonić metodę równości, gdy przesłonimy metodę hashCode w przypadku HashMap? W każdym razie skrót mapy zastępuje wartość, jeśli kod skrótu obiektu jest równy.
Vikas Verma
Hashapa @VikasVerma nie zastępuje żadnej wartości, jeśli hashcode obiektów jest równy, decyduje tylko o tym, gdzie należy umieścić nowo dodany obiekt do hashapa. Teraz w indeksie mogą znajdować się obiekty, więc aby uniknąć powielania, zastępujemy metodę equals i piszemy logikę określającą, kiedy dwa porównywane obiekty mają być traktowane jako równe. Jeśli nie zostanie to przesłonięte, to obiekty będą miały takie same wartości, ponieważ odniesienia do obu obiektów będą różne
Chetan
11

hashCode() :

Jeśli zastąpisz tylko metodę kodu skrótu, nic się nie stanie. Ponieważ zawsze zwraca nowy hashCodedla każdego obiektu jako klasy Object.

equals() :

Jeśli przesłonisz tylko metodę równości, a.equals(b)to prawda, że hashCodea i b muszą być takie same, ale nie mogą się zdarzyć. Ponieważ nie zastąpiłeś hashCodemetody.

Uwaga: hashCode()metoda klasy Object zawsze zwraca nowy hashCodedla każdego obiektu.

Więc kiedy musisz użyć swojego obiektu w kolekcji opartej na haszowaniu, musisz przesłonić oba equals()i hashCode().

Rinkal Gupta
źródło
To interesujący punkt, o zastąpieniu tylko hashCode () . Jest całkowicie w porządku, prawda? Czy mogą też występować problematyczne przypadki?
Johnny,
1
To jest myląca i zła odpowiedź. Przesłanianie (= only =) hashCode () zapewnia, że ​​każdy obiekt, który jest tworzony w odpowiedniej klasie o podobnych właściwościach, ma ten sam kod skrótu. Ale nie przyda się, ponieważ żaden z nich nie będzie sobie równy.
mfaisalhyder
8

Java ustanawia regułę, że

„Jeśli dwa obiekty są równe za pomocą metody Object class equals, wówczas metoda hashcode powinna dać taką samą wartość dla tych dwóch obiektów.”

Tak więc, jeśli w naszej klasie przesłonimy equals(), powinniśmy przesłonić hashcode()metodę, aby przestrzegać tej reguły. Obie metody, equals()i hashcode()są wykorzystywane w Hashtable, na przykład, do przechowywania wartości jako par klucz-wartość. Jeśli zastąpimy jedno, a nie drugie, istnieje możliwość, że Hashtablemogą one nie działać tak, jak chcemy, jeśli użyjemy takiego obiektu jako klucza.

Ritesh Kaushik
źródło
6

Ponieważ jeśli ich nie zastąpisz, zastosujesz domyślną implantację w Object.

Biorąc pod uwagę, że równość instancji i wartości hascode na ogół wymagają wiedzy o tym, co składa się na obiekt, na ogół będą musiały zostać przedefiniowane w twojej klasie, aby miały jakiekolwiek konkretne znaczenie.

PaulJWilliams
źródło
6

Aby używać własnych obiektów klasy jako kluczy w kolekcjach takich jak HashMap, Hashtable itp., Powinniśmy przesłonić obie metody (hashCode () i equals ()), mając świadomość wewnętrznego działania kolekcji. W przeciwnym razie prowadzi to do błędnych wyników, których nie oczekujemy.

Prashanth
źródło
6

Dodanie do odpowiedzi @Lombo

Kiedy trzeba zastąpić equals ()?

Domyślna implementacja Object's equals () to

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

co oznacza, że ​​dwa obiekty będą uważane za równe tylko wtedy, gdy będą miały ten sam adres pamięci, co będzie prawdziwe tylko wtedy, gdy porównasz obiekt ze sobą.

Ale możesz rozważyć dwa obiekty takie same, jeśli mają taką samą wartość dla jednej lub więcej ich właściwości (zobacz przykład podany w odpowiedzi @Lombo).

Więc equals()w tych sytuacjach przejdziecie na manowce i dacie własne warunki równości.

Z powodzeniem zaimplementowałem equals () i działa świetnie. Dlaczego oni również proszą o zastąpienie hashCode ()?

No cóż, dopóki nie używasz kolekcji opartych na „Hash” w klasie zdefiniowanej przez użytkownika, jest w porządku. Jednak jakiś czas w przyszłości może chcesz użyć HashMapalbo HashSeta jeśli nie overridei „prawidłowo wdrożyć” hashCode () , to zbiór Hash opiera się nie działać będzie zgodnie z przeznaczeniem.

Zastąp tylko równa się (dodatek do odpowiedzi @Lombo)

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

Przede wszystkim HashMap sprawdza, czy hashCode secondjest taki sam jak first. Tylko jeśli wartości są takie same, przejdzie do sprawdzenia równości w tym samym segmencie.

Ale tutaj hashCode jest inny dla tych 2 obiektów (ponieważ mają one inny adres pamięci niż domyślna implementacja). Dlatego nie będzie nawet obchodzić sprawdzania równości.

Jeśli masz punkt przerwania w przesłoniętej metodzie equals (), nie wkroczyłby, gdyby miały różne kody skrótu. contains()sprawdza hashCode()i tylko jeśli są takie same, wywołałoby to twoją equals()metodę.

Dlaczego nie możemy sprawić, by HashMap sprawdzał równość we wszystkich segmentach? Nie ma więc potrzeby zastępowania hashCode () !!

Brakuje Ci punktu kolekcji opartych na haszowaniu. Rozważ następujące :

Your hashCode() implementation : intObject%9.

Poniżej przedstawiono klucze przechowywane w postaci wiader.

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

Powiedzmy, że chcesz wiedzieć, czy mapa zawiera klucz 10. Czy chcesz przeszukać wszystkie wiadra? lub Czy chcesz przeszukać tylko jedną łyżkę?

Na podstawie kodu hashCode można stwierdzić, że jeśli 10 jest obecne, musi być obecne w segmencie 1. Zatem przeszukiwany będzie tylko segment 1!

użytkownik104309
źródło
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 („klucz”, „wartość”) obliczy wartość skrótu za pomocą hashCode()do określenia segmentu i użyje equals()metody, aby sprawdzić, czy wartość jest już obecna w segmencie . Jeśli nie, zostanie dodany, w przeciwnym razie zostanie zastąpiony bieżącą wartością
  • get („klucz”) użyje hashCode()najpierw do znalezienia Wpisu (segmentu) i equals()do znalezienia wartości we Wpisie

jeśli oba zostaną zastąpione,

Mapa < >

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

jeśli równa się nie zostanie zastąpione

Mapa < >

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

Jeśli hashCode nie zostanie zastąpione

Mapa < >

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. Dwa klucze równe według równej metody powinny wygenerować ten sam kod skrótu
  2. Dwa klucze generujące ten sam kod skrótu nie muszą być równe (w powyższym przykładzie wszystkie liczby parzyste generują ten sam kod skrótu)
bharanitharan
źródło
4

Rozważ zbieranie piłek w wiadrze w kolorze czarnym. Twoim zadaniem jest pokolorowanie tych kulek w następujący sposób i użycie ich do odpowiedniej gry,

Do tenisa - żółty, czerwony. Do krykieta - biały

Teraz wiadro ma kulki w trzech kolorach: żółtym, czerwonym i białym. I że teraz zrobiłeś kolorowanie Tylko ty wiesz, który kolor jest dla której gry.

Farbowanie kul - Hashowanie. Wybór piłki do gry - równa się.

Jeśli zrobiłeś kolorowanie i ktoś wybrał piłkę do gry w krykieta lub tenisa, to nie przeszkadza kolor !!!

bharanitharan
źródło
4

Patrzyłem na wyjaśnienie „Jeśli tylko przesłonisz hashCode, to kiedy wywołujesz myMap.put(first,someValue), bierze on pierwszy, oblicza jego hashCode i przechowuje go w danym segmencie. Następnie, gdy wywołujesz myMap.put(first,someOtherValue), należy go zastąpić drugim, zgodnie z Dokumentacją mapy, ponieważ są one równe (zgodnie z naszą definicją). ” :

Myślę, że drugi raz, kiedy dodajemy myMap, powinien to być „drugi” obiekt jakmyMap.put(second,someOtherValue)

Narinder
źródło
4

1) Typowy błąd pokazano w poniższym przykładzie.

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")));
    }
}

zielony samochód nie został znaleziony

2. Problem spowodowany przez hashCode ()

Problem jest spowodowany przez nie zastąpioną metodę hashCode(). Umowa między equals()i hashCode()jest:

  1. Jeśli dwa obiekty są równe, muszą mieć ten sam kod skrótu.
  2. Jeśli dwa obiekty mają ten sam kod skrótu, mogą być lub nie być równe.

    public int hashCode(){  
      return this.color.hashCode(); 
    }
Neeraj Gahlawat
źródło
4

Jest to przydatne podczas korzystania z obiektów wartości . Oto fragment z Portland Pattern Repository :

Przykładami obiektów wartości są liczby, daty, pieniądze i ciągi znaków. Zwykle są to małe przedmioty, które są dość szeroko stosowane. Ich tożsamość opiera się raczej na ich stanie niż na tożsamości obiektu. W ten sposób możesz mieć wiele kopii tego samego obiektu wartości konceptualnej.

Mogę mieć wiele kopii obiektu reprezentującego datę 16 stycznia 1998 r. Każda z tych kopii będzie sobie równa. W przypadku takiego małego obiektu często łatwiej jest tworzyć nowe i przenosić je, niż polegać na pojedynczym obiekcie reprezentującym datę.

Obiekt wartości powinien zawsze zastępować .equals () w Javie (lub = w Smalltalk). (Pamiętaj również o zastąpieniu .hashCode ()).

Ionuț G. Stan
źródło
3

Załóżmy, że masz klasę (A), która agreguje dwie inne (B) (C), i musisz przechowywać instancje (A) w tablicy hasht. Domyślna implementacja pozwala jedynie na rozróżnienie instancji, ale nie według (B) i (C). Tak więc dwa wystąpienia A mogą być równe, ale domyślnie nie pozwala na ich prawidłowe porównanie.

Zroszony
źródło
3

Metody równe i kod skrótu są zdefiniowane w klasie obiektowej. Domyślnie, jeśli metoda równa zwraca true, system przejdzie dalej i sprawdzi wartość kodu skrótu. Jeśli kod skrótu 2 obiektów jest również taki sam, wówczas obiekty zostaną uznane za takie same. Jeśli więc zastąpisz tylko metodę równości, to nawet jeśli metoda przesłonięcia równości wskazuje 2 obiekty, które mają być równe, zdefiniowany przez system kod skrótu może nie wskazywać, że 2 obiekty są równe. Musimy więc również przesłonić kod skrótu.

Aarti
źródło
Jeśli metoda równa zwraca wartość true, nie trzeba sprawdzać kodu skrótu. Jeśli jednak dwa obiekty mają różne kody skrótu, można je traktować jako różne bez konieczności wywoływania równości. Ponadto wiedza, że ​​żadna z rzeczy na liście nie ma określonego kodu skrótu, oznacza, że ​​żadna z rzeczy na liście nie może pasować do żadnego obiektu z tym kodem skrótu. Jako prosty przykład, jeśli ktoś ma listę obiektów, których kodami skrótu są liczby parzyste, oraz listę obiektów, których są liczbami nieparzystymi, żaden obiekt, którego kodem skrótu jest liczba parzysta, nie będzie na drugiej liście.
supercat
Jeśli jeden miał dwa obiekty X i Y, których metody „równe” wskazywały, że pasują, ale kod skrótu X był liczbą parzystą, a kod skrótu Y był liczbą nieparzystą, zbiór jak opisano powyżej, w którym zauważono, że kod skrótu obiektu Y był nieparzysty i przechowywany to na drugiej liście nie byłby w stanie znaleźć dopasowania dla obiektu X. Zauważyłby, że kod skrótu X był parzysty, a ponieważ druga lista nie zawiera żadnych obiektów z kodami skrótu o parzystych liczbach, nie przeszkadzałoby szukać tam czegoś, co pasuje do X, nawet jeśli Y pasuje do X. Co powinieneś powiedzieć ...
supercat
... byłoby tak, że wiele kolekcji unikałoby porównywania rzeczy, których kody skrótu sugerowałyby, że nie mogą być one równe. Biorąc pod uwagę dwa obiekty, których kody hash są nieznane, często jest to szybsze, aby porównać je bezpośrednio niż obliczeniowych ich kody hash, więc nie ma gwarancji, że rzeczy, które zgłaszają nierówne kody hash, ale powrót truedo equalsnie będą traktowane jako dopasowywania. Z drugiej strony, jeśli zdarzają się zbiory, zauważ, że rzeczy nie mogą mieć tego samego kodu skrótu, prawdopodobnie nie zauważą, że są równe.
supercat
3

Metody Equals i Hashcode w Javie

Są to metody klasy java.lang.Object, która jest superklasą wszystkich klas (także klas niestandardowych i innych zdefiniowanych w API Java).

Realizacja:

public boolean equals (Object obj)

public int hashCode ()

wprowadź opis zdjęcia tutaj

public boolean equals (Object obj)

Ta metoda sprawdza po prostu, czy dwa odwołania do obiektów xiy odnoszą się do tego samego obiektu. tj. Sprawdza, czy x == y.

Jest zwrotny: dla każdej wartości odniesienia x, x.equals (x) powinno zwracać wartość true.

Jest symetryczny: dla dowolnych wartości referencyjnych x i y wartość x.equals (y) powinna zwracać wartość true tylko wtedy, gdy y.equals (x) zwraca wartość true.

Jest przechodnie: dla dowolnych wartości referencyjnych x, y i z, jeśli x.equals (y) zwraca true, a y.equals (z) zwraca true, to x.equals (z) powinno zwracać true.

Jest spójny: dla dowolnej wartości referencyjnej x i y wielokrotne wywołania x.equals (y) konsekwentnie zwracają wartość true lub konsekwentnie zwracają wartość false, pod warunkiem, że żadna informacja używana w porównaniach równości obiektu nie jest modyfikowana.

Dla każdej wartości odniesienia innej niż null x, x.equals (null) powinno zwracać wartość false.

public int hashCode ()

Ta metoda zwraca wartość kodu skrótu dla obiektu, na który wywoływana jest ta metoda. Ta metoda zwraca wartość kodu skrótu jako liczbę całkowitą i jest obsługiwana na korzyść klas kolekcji opartych na haszowaniu, takich jak Hashtable, HashMap, HashSet itp. Metodę tę należy zastąpić w każdej klasie, która przesłania metodę równości.

Ogólna umowa hashCode to:

Za każdym razem, gdy jest wywoływana w tym samym obiekcie więcej niż jeden raz podczas wykonywania aplikacji Java, metoda hashCode musi konsekwentnie zwracać tę samą liczbę całkowitą, pod warunkiem, że żadna informacja użyta w porównaniach równych obiektu nie jest modyfikowana.

Ta liczba całkowita nie musi pozostać spójna od jednego wykonania aplikacji do innego wykonania tej samej aplikacji.

Jeśli dwa obiekty są równe zgodnie z metodą equals (Object), wówczas wywołanie metody hashCode na każdym z dwóch obiektów musi dać ten sam wynik w postaci liczby całkowitej.

Nie jest wymagane, aby jeśli dwa obiekty były nierówne zgodnie z metodą equals (java.lang.Object), wówczas wywołanie metody hashCode na każdym z dwóch obiektów musi dawać różne wyniki liczb całkowitych. Programiści powinni jednak pamiętać, że uzyskiwanie wyraźnych wyników liczb całkowitych dla nierównych obiektów może poprawić wydajność tablic mieszających.

Równe obiekty muszą generować ten sam kod skrótu, o ile są one równe, jednak nierówne obiekty nie muszą tworzyć odrębnych kodów skrótu.

Zasoby:

JavaRanch

Obrazek

Affy
źródło
Obraz (link wideo) jest w trybie prywatnym. Udostępnij publicznie.
UdayKiran Pulipati
2

W poniższym przykładzie, jeśli skomentujesz zastąpienie dla equals lub hashcode w klasie Person, ten kod nie sprawdzi kolejności Toma. Korzystanie z domyślnej implementacji kodu skrótu może powodować błędy w przeglądaniu skrótów.

Mam poniżej uproszczony kod, który wyświetla zamówienie ludzi według Person. Osoba jest używana jako klucz w tablicy mieszającej.

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);
        }
    }
}
developer747
źródło
2

Klasy ciągów i klasy opakowań mają różną implementację equals()ihashCode() metody niż klasa Object. Metoda equals () klasy Object porównuje odwołania do obiektów, a nie do zawartości. Metoda hashCode () klasy Object zwraca odrębny kod skrótu dla każdego obiektu, niezależnie od tego, czy zawartość jest taka sama.

Prowadzi to do problemu, gdy używasz kolekcji Map, a klucz jest typu Trwałego, typu StringBuffer / builder. Ponieważ nie zastępują equals () i hashCode () w przeciwieństwie do klasy String, equals () zwróci false, gdy porównasz dwa różne obiekty, mimo że oba mają tę samą zawartość. Sprawi, że hashMap będzie przechowywać te same klucze zawartości. Przechowywanie tych samych kluczy treści oznacza, że ​​narusza zasadę Mapy, ponieważ Mapa w ogóle nie zezwala na duplikowanie kluczy. Dlatego zastępujesz metody equals () oraz hashCode () w swojej klasie i udostępniasz implementację (IDE może wygenerować te metody), aby działały tak samo jak String's equals () i hashCode () i zapobiegały tym samym kluczom treści.

Musisz zastąpić metodę hashCode () wraz z equals (), ponieważ equals () działa zgodnie z hashcode.

Ponadto przesłanianie metody hashCode () wraz z equals () pomaga zachować nienaruszony kontrakt equals () - hashCode (): „Jeśli dwa obiekty są równe, muszą mieć ten sam kod skrótu”.

Kiedy trzeba napisać niestandardową implementację dla hashCode ()?

Jak wiemy, wewnętrzne działanie HashMap opiera się na zasadzie Hashing. Istnieją pewne segmenty, w których zapisywane są zestawy danych. Dostosowujesz implementację hashCode () zgodnie z wymaganiami, aby obiekty tej samej kategorii mogły być przechowywane w tym samym indeksie. gdy przechowujesz wartości w kolekcji Map za pomocąput(k,v) metody, wewnętrzna implementacja put () to:

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

Oznacza to, że generuje indeks, a indeks jest generowany na podstawie kodu skrótu określonego obiektu klucza. Tak więc, aby ta metoda generowała kod skrótu zgodnie z wymaganiami, ponieważ te same zestawy kodów zostaną zapisane w tym samym segmencie lub indeksie.

Otóż ​​to!

Arun Raaj
źródło
1

hashCode()Metoda służy do uzyskania unikalnej liczby całkowitej dla danego obiektu. Ta liczba całkowita jest używana do określania położenia segmentu, gdy ten obiekt musi być przechowywany w niektórych HashTable, HashMaptakich jak struktura danych. Domyślnie Object'shashCode() metoda zwraca i całkowitą reprezentację adresu pamięci, w którym przechowywany jest obiekt.

hashCode()Metoda obiektów jest używany, gdy wstawiamy je do HashTable, HashMaplub HashSet. Więcej na tematHashTables na Wikipedii w celach informacyjnych.

Aby wstawić dowolny wpis w strukturze danych mapy, potrzebujemy zarówno klucza, jak i wartości. Jeśli zarówno klucz, jak i wartości są typami danych definiowanymi przez użytkownika, hashCode()klucz określa, gdzie należy przechowywać obiekt wewnętrznie. Gdy konieczne będzie również wyszukanie obiektu na mapie, kod skrótu klucza określi, gdzie szukać obiektu.

Kod skrótu wskazuje tylko wewnętrznie pewien „obszar” (lub listę, segment itd.). Ponieważ różne obiekty kluczy mogą potencjalnie mieć ten sam kod skrótu, sam kod skrótu nie gwarantuje, że zostanie znaleziony odpowiedni klucz. HashTablePotem iteracje ten obszar (wszystkie klawisze z tym samym kodem hash) i korzysta z kluczykaequals() metodę, aby znaleźć odpowiedni klucz. Po znalezieniu odpowiedniego klucza zwracany jest obiekt przechowywany dla tego klucza.

Tak więc, jak widzimy, kombinacja metod hashCode()i equals()jest używana podczas przechowywania i wyszukiwania obiektów w pliku HashTable.

UWAGI:

  1. Zawsze używaj tych samych atrybutów obiektu do wygenerowania hashCode()i equals()obu. Podobnie jak w naszym przypadku, użyliśmy identyfikatora pracownika.

  2. equals() musi być spójny (jeśli obiekty nie są modyfikowane, wówczas musi zwracać tę samą wartość).

  3. Ilekroć a.equals(b), a.hashCode()musi być taki sam jak b.hashCode().

  4. Jeśli zastąpisz jedno, powinieneś zastąpić drugie.

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

Paramesh Korrakuti
źródło
hashCode()nie jest używany do zwracania unikalnej liczby całkowitej dla każdego obiektu. To jest niemożliwe. Sam temu zaprzeczasz w drugim zdaniu czwartego akapitu.
Markiz Lorne
@EJP, przez większość czasu hascode () zwraca unikalny interger dla dwóch różnych obiektów. Ale będą szanse na zderzenie hascode dla dwóch różnych obiektów, ta koncepcja nazywa się kolizją Hashcode . Proszę odnieść się do: tech.queryhome.com/96931/…
Paramesh Korrakuti
1

IMHO, to zgodnie z regułą - jeśli dwa obiekty są równe, powinny mieć taki sam skrót, tzn. Równe obiekty powinny wytwarzać równe wartości skrótu.

Biorąc pod uwagę powyższe, default equals () w Object jest ==, który dokonuje porównania adresu, hashCode () zwraca adres w liczbie całkowitej (skrót w rzeczywistym adresie), który jest znowu odrębny dla odrębnego Object.

Jeśli chcesz użyć niestandardowych obiektów w kolekcjach opartych na skrócie, musisz przesłonić zarówno equals () i hashCode (), przykład Jeśli chcę zachować zestaw skrótów obiektów pracownika, jeśli nie użyję silniejszego kodu skrótu i ​​równa się Mogę ostatecznie zastąpić dwa różne obiekty pracownika, dzieje się tak, gdy używam wieku jako hashCode (), jednak powinienem używać unikalnej wartości, którą może być identyfikator pracownika.

Cleonjoys
źródło
1

Aby pomóc Ci w sprawdzeniu zduplikowanych obiektów, potrzebujemy niestandardowego znaku równości i kodu skrótu.

Ponieważ hashcode zawsze zwraca liczbę, zawsze jest on w stanie szybko pobrać obiekt za pomocą liczby zamiast klawisza alfabetycznego. Jak to zrobi Załóżmy, że utworzyliśmy nowy obiekt, przekazując pewną wartość, która jest już dostępna w innym obiekcie. Teraz nowy obiekt zwróci tę samą wartość skrótu co inny obiekt, ponieważ przekazana wartość jest taka sama. Po zwróceniu tej samej wartości skrótu JVM za każdym razem przechodzi do tego samego adresu pamięci, a jeśli w przypadku tej samej wartości skrótu występuje więcej niż jeden obiekt, użyje metody equals () do zidentyfikowania poprawnego obiektu.

Tavash
źródło
1

Jeśli chcesz przechowywać i odzyskiwać swój niestandardowy obiekt jako klucz w Mapie, zawsze powinieneś nadpisywać equals i hashCode w swoim niestandardowym obiekcie. Na przykład:

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");

Tutaj p1 i p2 będą traktowane jako tylko jeden obiekt, a maprozmiar będzie wynosił tylko 1, ponieważ są one równe.

Ambrish Rajput
źródło
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());
    }

}

Klasa testowa

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));
    }

}

W klasie obiektowej równa się (Object obj) służy do porównywania porównania adresów, dlatego w klasie testowej, jeśli porównujesz dwa obiekty, to równa się metoda dająca fałsz, ale gdy zastąpimy hashcode (), może porównać zawartość i dać odpowiedni wynik.

Manash Ranjan Dakua
źródło
i Test klasy I dodałem w poniższym programie.
Manash Ranjan Dakua
W klasie obiektowej równa się (Object obj) służy do porównania porównania adresów, dlatego w klasie testowej, jeśli porównujesz dwa obiekty, to równa się metoda dająca fałsz, ale gdy zastąpimy hashcode (), może porównać zawartość i dać odpowiedni wynik.
Manash Ranjan Dakua
1
możesz użyć linku edycji tuż poniżej tej odpowiedzi, aby dodać do swojej odpowiedzi. Nie dodawaj odpowiedzi jako dwóch niekompletnych
Suraj Rao,
1

Jeśli przesłonisz equals()i nie hashcode(), nie znajdziesz żadnego problemu, chyba że ty lub ktoś inny użyje tego typu klasy w takiej kolekcji mieszanej HashSet. Ludzie przede mną wielokrotnie wyjaśniali udokumentowaną teorię, jestem tutaj, aby podać bardzo prosty przykład.

Zastanów się nad klasą, która equals()musi oznaczać coś dostosowanego:

    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();
        }

    }

Teraz rozważ tę główną klasę:

    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());
        }

    }

To da następujące wyniki:

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

Cieszę się z wyników. Ale jeśli nie przesłonię hashCode(), spowoduje to koszmar, ponieważ obiekty o tej Rishavsamej zawartości członka nie będą już traktowane jako unikalne, ponieważ hashCodebędą się różnić, ponieważ są generowane domyślnie, oto wynik:

    true
    -----------------------------------
    false
    -----------------------------------
    2
91StarSky
źródło
0

Obie metody są zdefiniowane w klasie Object. Oba są w najprostszej realizacji. Więc kiedy potrzebujesz, chcesz dodać więcej implementacji do tych metod, wtedy masz nadpisanie w swojej klasie.

Dla Ex: metoda equals () w obiekcie sprawdza tylko równość w odniesieniu do referencji. Więc jeśli potrzebujesz również porównać jego stan, możesz to zmienić, tak jak dzieje się to w klasie String.

GuruKulki
źródło
-3

Bah - „Musisz zastąpić hashCode () w każdej klasie, która przesłania equals ().”

[z Effective Java, autor: Joshua Bloch?]

Czy to nie jest odwrotna sytuacja? Przesłanianie hashCode prawdopodobnie oznacza, że ​​piszesz klasę klucza skrótu, ale przesłonięcie równości na pewno nie. Istnieje wiele klas, które nie są używane jako klucze skrótu, ale chcą metody testowania równości logicznej z innego powodu. Jeśli wybierzesz dla niego opcję „równa się”, możesz zostać upoważniony do napisania implementacji hashCode poprzez nadgorliwe stosowanie tej reguły. Wszystko, co osiąga, to dodawanie nieprzetestowanego kodu do bazy kodów, zło czekające na potknięcie się w przyszłości. Pisanie kodu, którego nie potrzebujesz, jest również zwinny. To po prostu źle (a ide wygenerowany prawdopodobnie nie będzie kompatybilny z twoimi ręcznie stworzonymi równościami).

Czy na pewno powinni byli ustanowić interfejs dla obiektów napisanych jako klucze? Niezależnie od tego, Object nigdy nie powinien zapewniać domyślnego hashCode () i equals () imho. Prawdopodobnie zachęciło to wiele zepsutych kolekcji skrótów.

Ale w każdym razie myślę, że „zasada” jest zapisana od początku. W międzyczasie będę nadal unikać używania metody „równa się” do metod testowania równości :-(

Alan Dobbie
źródło