JavaRush /Blog Java /Random-PL /Praca z metodami hashCode() i równości() w Javie
Lenchik854
Poziom 0
Chernihiv

Praca z metodami hashCode() i równości() w Javie

Opublikowano w grupie Random-PL
W tym poście przedstawię moje zrozumienie metod hashCode()i plików equals(). Chcę porozmawiać o ich domyślnej implementacji, a także o tym, jak poprawnie je zastąpić. Napiszę także o implementacji tych metod przy użyciu klas pomocniczych pakietu Apache Common. Praca z metodami hashCode() i równości() w Javie - 1Treść tego wpisu:
  1. Używanie hashCode()i equals().
  2. Zastąp zachowanie domyślne.
  3. Zastępowanie hashCode()i equals()używanie Apache Commons Lang.
  4. Coś, o czym warto pamiętać.
  5. Szczególna uwaga podczas korzystania z ORM.
Metody hashCode()i equals()zostały zdefiniowane w klasie Object, która jest klasą nadrzędną dla obiektów Java. Dlatego wszystkie obiekty Java dziedziczą domyślną implementację z tych metod.

Używanie hashCode() i równości()

Metoda hashCode()służy do uzyskania unikalnej liczby całkowitej dla danego obiektu. Kiedy obiekt musi być przechowywany jako struktura danych w tabeli mieszającej (zwanej także wiadrem), liczba ta służy do określenia jego lokalizacji w tej tabeli. Domyślnie metoda hashCode()obiektu zwraca numer komórki pamięci, w której przechowywany jest obiekt. Metoda equals(), jak sama nazwa wskazuje, służy po prostu do sprawdzenia równości dwóch obiektów. Domyślna implementacja tej metody po prostu sprawdza referencje dwóch obiektów, aby sprawdzić, czy są one równoważne.

Zastępowanie zachowania domyślnego

Wszystko działa dobrze, o ile nie zastąpisz żadnej z tych metod w swoich klasach. Czasami jednak aplikacje muszą zmienić domyślne zachowanie niektórych obiektów. Weźmy przykład, w którym masz plik Employee. Napiszmy minimalną możliwą strukturę takiej klasy.
public class Employee
{
    private Integer id;
    private String firstname;
    private String lastName;
    private String department;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getFirstname() {
        return firstname;
    }
    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getDepartment() {
        return department;
    }
    public void setDepartment(String department) {
        this.department = department;
    }
}
Klasa opisana powyżej Employeeposiada kilka podstawowych atrybutów i metod akcesorów. Przyjrzyjmy się teraz prostej sytuacji, w której musimy porównać dwa obiekty klasy Employee.
public class EqualsTest {
    public static void main(String[] args) {
        Employee e1 = new Employee();
        Employee e2 = new Employee();

        e1.setId(100);
        e2.setId(100);
        //Печатает false в консоли
        System.out.println(e1.equals(e2));
    }
}
Nie trzeba jasnowidza, aby zgadnąć, że powyższa metoda zwróci wartość „false”. Ale czy jest to rzeczywiście poprawne, biorąc pod uwagę, że te dwa obiekty są takie same? W aplikacji czasu rzeczywistego metoda musi zwracać wartość true. Aby osiągnąć prawidłowe zachowanie, musimy zastąpić metodę equals(), jak pokazano poniżej:
public boolean equals(Object o) {
        if(o == null)
        {
            return false;
        }
        if (o == this)
        {
           return true;
        }
        if (getClass() != o.getClass())
        {
            return false;
        }
        Employee e = (Employee) o;
        return (this.getId() == e.getId());
}
Dodaj tę metodę do swojej klasy Employee, a sprawdzenie równoważności zwróci wartość „true”. Czy jednak zrobiliśmy wszystko? Jeszcze nie. Przetestujmy naszą zmodyfikowaną klasę jeszcze w jeden sposób.
import java.util.HashSet;
import java.util.Set;

public class EqualsTest
{
    public static void main(String[] args)
    {
        Employee e1 = new Employee();
        Employee e2 = new Employee();

        e1.setId(100);
        e2.setId(100);

        //Печатает 'true'
        System.out.println(e1.equals(e2));

        Set employees = new HashSet();
        employees.add(e1);
        employees.add(e2);
        //Печатает два obiektа
        System.out.println(employees);
    }
}
Polecenie System.out.println(employee)drukuje dwa obiekty. Jeżeli oba obiekty były równoważne i Setzawierały tylko obiekty unikalne, to HashSetwewnątrz powinna znajdować się tylko jedna instancja, tj. oba obiekty odnoszą się do tych samych instancji klasy Employee. Co przegapiliśmy? Pominęliśmy drugą ważną metodę hashCode(). Jak mówi dokumentacja Java, jeśli zastąpisz metodę equals(), musisz zastąpić metodę hashCode(). Dodajmy więc kolejną metodę do naszej klasy Employee.
@Override
 public int hashCode()
 {
    final int PRIME = 31;
    int result = 1;
    result = PRIME * result + getId();
    return result;
 }
Dodaliśmy tę metodę raz do naszej klasy i zostanie wydrukowany tylko jeden obiekt, a zatem sprawdzenie równoważności e1 i e2 okazało się prawdą.

Zastępowanie hashCode()i equals()używanie Apache Commons Lang

Apache Commons udostępnia dwie świetne klasy pomocnicze do wywoływania metod hashCode()i equals(). Poniżej widzimy użycie:
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Employee
{
 private Integer id;
 private String firstname;
 private String lastName;
 private String department;
public Integer getId() {
    return id;
 }
 public void setId(Integer id) {
    this.id = id;
 }
 public String getFirstname() {
    return firstname;
 }
 public void setFirstname(String firstname) {
    this.firstname = firstname;
 }
 public String getLastName() {
    return lastName;
 }
 public void setLastName(String lastName) {
    this.lastName = lastName;
 }
 public String getDepartment() {
    return department;
 }
 public void setDepartment(String department) {
    this.department = department;
 }
@Override
 public int hashCode()
 {
    final int PRIME = 31;
    return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).
           toHashCode();
 }
@Override
 public boolean equals(Object o) {
    if (o == null)
       return false;
    if (o == this)
       return true;
    if (o.getClass() != getClass())
       return false;
    Employee e = (Employee) o;
       return new EqualsBuilder().
              append(getId(), e.getId()).
              isEquals();
    }
 }
Z drugiej strony, jeśli korzystasz z jednego z edytorów kodu, powinien on również być w stanie wywołać dla Ciebie kilka ładnych struktur. Na przykład, jeśli w Eclipse IDE klikniesz prawym przyciskiem myszy klasę >> źródło > Generowanie hashCode() i równości() ... wygeneruje dla ciebie bardzo ładną implementację. Praca z metodami hashCode() i równości() w Javie - 2Coś, o czym warto pamiętać.
  1. Zawsze używaj tych samych atrybutów obiektu, aby wywołać zarówno and hashCode()and equals(). Tylko w naszym przypadku użyliśmy employee id.
  2. Metoda equals()musi być trwała (jeżeli obiekt się nie zmienił, metoda musi zwrócić tę samą wartość).
  3. Kiedykolwiek a.equals(b), to a.hashCode()musi być takie samo jak b.hashCode().
  4. Jeśli zastąpisz jedną metodę, musisz zastąpić drugą.

Szczególna uwaga podczas korzystania z ORM

Jeśli masz do czynienia z ORM (ru.wikipedia.org/wiki/ORM), zawsze używaj modułów pobierających i nigdy nie używaj odwołań do pól hashCode(). equals()Dzieje się tak dlatego, że w ORM-ie od czasu do czasu pola są ładowane przy użyciu leniwego ładowania i nie są dostępne, dopóki nie zostaną wywołane ich moduły pobierające. Na przykład w naszej klasie Employeeużywamy e1.id == e2.id. Jest całkowicie możliwe, że ipola d są ładowane przy użyciu leniwego ładowania. Jedno z pól może mieć wartość 0 lub null i otrzymamy nieprawidłowe zachowanie. Jeśli jednak zostanie użyte e1.getId() == e2.getId(), możemy być pewni, nawet jeśli pola zostały załadowane przy użyciu leniwego ładowania; wywołanie modułu pobierającego najpierw wypełni pole. To wszystko, co wiem o metodach hashCode()and equals(). Mam nadzieję, że to komuś gdzieś pomoże. Powodzenia z Twoimi studiami!! ps. To moja pierwsza próba tłumaczenia. Starałem się przekazać wszystko jak najbliżej tego, co chciał powiedzieć autor. Jeśli masz jakieś uwagi, napisz je w komentarzach. Nie oceniaj ściśle :-))) Artykuł oryginalny
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION