JavaRush /Blog Java /Random-PL /Przerwa kawowa #168. Po co zastępować metody równości i h...

Przerwa kawowa #168. Po co zastępować metody równości i hashcode w Javie?

Opublikowano w grupie Random-PL

Po co zastępować metody równości i hashcode w Javie?

Źródło: Medium W tym artykule skupiono się na dwóch blisko powiązanych metodach: quals() i hashcode() . Dowiesz się, jak współdziałają ze sobą i jak prawidłowo je zastąpić. Przerwa kawowa #168.  Po co zastępować metody równości i hashcode w Javie?  - 1

Dlaczego zastępujemy metodę równości()?

W Javie nie możemy przeciążać zachowania operatorów takich jak == , += , -+ . Działają według zadanego procesu. Rozważmy na przykład operację operatora == .

Jak działa operator ==?

Sprawdza, czy dwa porównywane odniesienia wskazują na tę samą instancję w pamięci. Operator == zwróci wartość true tylko wtedy, gdy dwa odniesienia reprezentują tę samą instancję w pamięci. Rzućmy okiem na przykładowy kod:
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
      }
Załóżmy, że w swoim programie utworzyłeś dwa obiekty Person w różnych miejscach i chcesz je porównać.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
Z biznesowego punktu widzenia oba wyglądają tak samo, prawda? Ale w przypadku JVM nie są one takie same. Ponieważ oba są tworzone przy użyciu słowa kluczowego new , te instancje znajdują się w różnych segmentach pamięci. Dlatego operator == zwróci false . Ale jeśli nie możemy zastąpić operatora == , to jak powiedzieć maszynie JVM, że chcemy, aby te dwa obiekty były traktowane tak samo? W tym miejscu do gry wchodzi metoda .equals() . Możesz zastąpić funkcję równości() , aby sprawdzić, czy niektóre obiekty mają te same wartości dla niektórych pól, aby uznać je za równe. Możesz wybrać, które pola chcesz porównać. Jeśli powiemy, że dwa obiekty Person będą takie same tylko wtedy, gdy będą miały ten sam wiek i tę samą nazwę, wówczas IDE wygeneruje coś takiego , aby automatycznie utworzyć równa() :
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
Wróćmy do naszego poprzedniego przykładu.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
Tak, nie możemy przeciążać operatora == , aby porównywać obiekty tak, jak chcemy, ale Java daje nam inny sposób - metodę równości() , którą możemy zastąpić według własnego uznania. Należy pamiętać, że jeśli nie udostępnimy w naszej klasie niestandardowej wersji funkcji .equals() (znanej również jako przesłonięcie), wówczas predefiniowana funkcja .equals() z klasy Object i operator == będą zachowywać się tak samo. Domyślna metoda równości() , odziedziczona z Object , sprawdza, czy obie porównywane instancje są takie same w pamięci!

Dlaczego zastępujemy metodę hashCode()?

Niektóre struktury danych w Javie, takie jak HashSet i HashMap , przechowują swoje elementy w oparciu o funkcję skrótu zastosowaną do tych elementów. Funkcja skrótu to hashCode() . Jeśli mamy wybór nadpisania metody .equals() , powinniśmy mieć także możliwość nadpisania metody hashCode() . Jest ku temu powód. W końcu domyślna implementacja funkcji hashCode() , odziedziczona z Object , traktuje wszystkie obiekty w pamięci jako unikalne! Wróćmy jednak do tych struktur danych skrótu. Istnieje reguła dotycząca tych struktur danych. HashSet nie może zawierać zduplikowanych wartości, a HashMap nie może zawierać zduplikowanych kluczy. HashSet jest implementowany przy użyciu HashMap w taki sposób, że każda wartość HashSet jest przechowywana jako klucz w HashMap . Jak działa HashMap ? HashMap to natywna tablica z wieloma segmentami. Każdy segment ma połączoną listę ( linkedList ). Ta połączona lista przechowuje nasze klucze. HashMap znajduje poprawną listę połączoną dla każdego klucza za pomocą metody hashCode() , a następnie przechodzi przez wszystkie elementy tej listy połączonej i stosuje metodę równości() do każdego z tych elementów, aby sprawdzić, czy element ten się tam zawiera. Duplikaty kluczy są niedozwolone. Przerwa kawowa #168.  Po co zastępować metody równości i hashcode w Javie?  - 2Kiedy umieścimy coś w HashMap , klucz jest przechowywany na jednej z połączonych list. Na której połączonej liście będzie przechowywany ten klucz, pokaże wynik metody hashCode() dla tego klucza. Oznacza to, że jeśli funkcja key1.hashCode() daje w wyniku 4, wówczas ten klucz1 zostanie zapisany w czwartym segmencie tablicy na istniejącej tam liście LinkedList . Domyślnie metoda hashCode() zwraca różne wyniki dla każdej instancji. Jeśli mamy domyślną funkcję równości() , która zachowuje się jak == i traktuje wszystkie instancje w pamięci jako różne obiekty, nie będzie problemu. Jak być może pamiętasz, w naszym poprzednim przykładzie powiedzieliśmy, że chcemy, aby instancje Person były uznawane za równe, jeśli ich wiek i imiona są takie same.
Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!
Utwórzmy teraz mapę do przechowywania tych instancji jako kluczy z określonym ciągiem jako parą wartości.
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
W klasie Person nie zastąpiliśmy metody hashCode , ale mamy nadpisaną metodę równości . Ponieważ domyślny hashCode daje różne wyniki dla różnych instancji Java person1.hashCode() i person2.hashCode() , istnieje duże prawdopodobieństwo uzyskania różnych wyników. Nasza mapa może kończyć się różnymi osobami na różnych połączonych listach. Przerwa kawowa #168.  Po co zastępować metody równości i hashcode w Javie?  - 3Jest to sprzeczne z logiką HashMap . W końcu HashMap nie może mieć kilku identycznych kluczy! Rzecz w tym, że domyślny hashCode() odziedziczony z klasy Object nie wystarczy. Nawet po zastąpieniu metody równości() klasy Person . Dlatego musimy zastąpić metodę hashCode() po zastąpieniu metody równości . Teraz naprawmy to. Musimy zastąpić naszą metodę hashCode() tak, aby uwzględniała te same pola co równa się() , a mianowicie wiek i imię .
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
@Override
public int hashCode() {
        int prime = 31;
        return prime*Objects.hash(name, age);
    }
W metodzie hashCode() zastosowaliśmy prostą wartość (można zastosować dowolne inne wartości). Sugeruje się jednak używanie liczb pierwszych, aby stworzyć mniej problemów. Spróbujmy ponownie zapisać te klucze w naszej HashMap :
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode() i person2.hashCode() będą takie same. Powiedzmy, że wynoszą 0. HashMap przejdzie do segmentu 0, a w nim LinkedList zapisze osobę1 jako klucz o wartości „1”. W drugim przypadku, gdy HashMap ponownie przejdzie do segmentu 0, aby zapisać klucz osoba2 o wartości „2”, zobaczy, że inny klucz mu równy już tam istnieje. W ten sposób nadpisze poprzedni klucz. I tylko kluczowa osoba2 będzie istnieć w naszej HashMap . W ten sposób dowiedzieliśmy się, jak działa reguła HashMap , która mówi, że nie można używać wielu identycznych kluczy! Należy jednak pamiętać, że nierówne instancje mogą mieć ten sam hashcode, a równe instancje muszą zwracać ten sam hashcode.Przerwa kawowa #168.  Po co zastępować metody równości i hashcode w Javie?  - 4
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION