JavaRush /Java Blog /Random-IT /Lavorare con i metodi hashCode() e equals() in Java
Lenchik854
Livello 0
Chernihiv

Lavorare con i metodi hashCode() e equals() in Java

Pubblicato nel gruppo Random-IT
In questo post illustrerò la mia comprensione dei metodi hashCode()e equals(). Voglio parlare della loro implementazione predefinita e di come sovrascriverli correttamente. Scriverò anche sull'implementazione di questi metodi utilizzando le classi helper del pacchetto Apache Common. Lavorare con i metodi hashCode() e equals() in Java - 1Contenuto di questo post:
  1. Utilizzando hashCode()e equals().
  2. Sostituisci il comportamento predefinito.
  3. Sovrascrivere hashCode()e equals()utilizzare Apache Commons Lang.
  4. Qualcosa che è importante ricordare.
  5. Attenzione speciale quando si utilizza ORM.
I metodi hashCode()e equals()sono stati definiti nella classe Object, che è la classe genitore per gli oggetti Java. Pertanto, tutti gli oggetti Java ereditano l'implementazione predefinita da questi metodi.

Utilizzando hashCode() ed equals()

Il metodo hashCode()viene utilizzato per ottenere un numero intero univoco per un determinato oggetto. Quando un oggetto deve essere archiviato come struttura dati in una tabella hash (chiamata anche bucket), questo numero viene utilizzato per determinare la sua posizione in quella tabella. Per impostazione predefinita, il metodo hashCode()per un oggetto restituisce il numero della posizione di memoria in cui è archiviato l'oggetto. Il metodo equals(), come suggerisce il nome, viene utilizzato per verificare semplicemente l'uguaglianza di due oggetti. L'implementazione predefinita di questo metodo controlla semplicemente i riferimenti di due oggetti per vedere se sono equivalenti.

Sostituire il comportamento predefinito

Tutto funziona bene finché non sovrascrivi nessuno di questi metodi nelle tue classi. Ma a volte le applicazioni devono modificare il comportamento predefinito di alcuni oggetti. Facciamo un esempio in cui hai un file Employee. Scriviamo la struttura minima possibile di tale classe.
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;
    }
}
La classe sopra descritta Employeeha alcuni attributi di base e metodi di accesso. Consideriamo ora una situazione semplice in cui dobbiamo confrontare due oggetti della classe 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));
    }
}
Non ci vuole un chiaroveggente per indovinare che il metodo sopra riportato restituirà “falso”. Ma è effettivamente corretto, dato che questi due oggetti sono la stessa cosa? Nell'applicazione in tempo reale, il metodo deve restituire true. Per ottenere il comportamento corretto, dobbiamo sovrascrivere il metodo equals(), come fatto di seguito:
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());
}
Aggiungi questo metodo alla tua classe Employeee il controllo di equivalenza restituirà "vero". Ma abbiamo fatto tutto? Non ancora. Testiamo la nostra classe modificata in un altro modo.
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);
        //Печатает два an object
        System.out.println(employees);
    }
}
Il comando System.out.println(employee)stampa due oggetti. Se entrambi gli oggetti fossero equivalenti e Setfossero contenuti solo oggetti univoci, allora HashSetdovrebbe esserci solo un'istanza all'interno, ad es. entrambi gli oggetti si riferiscono alle stesse istanze della classe Employee. Cosa ci è mancato? Abbiamo mancato il secondo metodo importante hashCode(). Come dice la documentazione Java, se sovrascrivi il metodo equals(), allora ti viene richiesto di sovrascrivere il metodo hashCode(). Quindi aggiungiamo un altro metodo alla nostra classe Employee.
@Override
 public int hashCode()
 {
    final int PRIME = 31;
    int result = 1;
    result = PRIME * result + getId();
    return result;
 }
Abbiamo aggiunto questo metodo una volta alla nostra classe e verrà stampato solo un oggetto e quindi il controllo dell'equivalenza di e1 ed e2 è risultato vero.

Sovrascrivere hashCode()e equals()utilizzare Apache Commons Lang

Apache Commons fornisce due ottime classi di supporto per chiamare metodi hashCode()e equals(). Di seguito vediamo l'utilizzo:
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();
    }
 }
D'altra parte, se usi uno degli editor di codice, dovrebbero anche essere in grado di richiamare alcune strutture carine. Ad esempio, se nell'IDE Eclipse fai clic con il pulsante destro del mouse su class >> source > Generating hashCode() ed equals() ... genererà un'implementazione molto interessante per te. Lavorare con i metodi hashCode() e equals() in Java - 2Qualcosa che è importante ricordare.
  1. Utilizzare sempre gli stessi attributi dell'oggetto per chiamare sia and hashCode()che equals(). Solo nel nostro caso, abbiamo usato employee id.
  2. Il metodo equals()deve essere persistente (se l'oggetto non è cambiato, il metodo deve restituire lo stesso valore).
  3. Ogni volta che a.equals(b), allora a.hashCode()deve essere lo stesso di b.hashCode().
  4. Se si sovrascrive un metodo, è necessario sovrascrivere anche il secondo.

Attenzione speciale quando si utilizza ORM

Se hai a che fare con ORM (ru.wikipedia.org/wiki/ORM), usa sempre i getter e non usare mai i riferimenti ai campi hashCode(). equals()Questo perché in un ORM, di tanto in tanto i campi vengono caricati utilizzando il caricamento lento e non sono accessibili finché non vengono chiamati i relativi getter. Ad esempio, nella nostra classe Employeeusiamo e1.id == e2.id. È del tutto possibile che i icampi d vengano caricati utilizzando il caricamento lento. Uno dei campi potrebbe essere 0 o null e otterremo un comportamento errato. Ma, se viene utilizzato e1.getId() == e2.getId(), possiamo essere sicuri anche se i campi sono stati caricati utilizzando il caricamento lento; chiamare il getter popolerà prima il campo. Questo è tutto quello che so sui metodi hashCode()e equals(). Spero che questo aiuti qualcuno da qualche parte. Buona fortuna con i tuoi studi!! ps Questo è il mio primo tentativo di traduzione. Ho cercato di trasmettere tutto il più vicino possibile a ciò che l'autore voleva dire. Se avete commenti, scrivete nei commenti. Non giudicare rigorosamente :-))) Articolo originale
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION