Domanda

Ho una classe la cui uguaglianza si basa su 2 campi in modo tale che se uno dei due è uguale, gli oggetti di questo tipo sono considerati uguali.come posso scrivere una funzione hashCode() per tale equals() in modo che venga preservato il contratto generale di hashCode uguale quando equals restituisce true?

public class MyClass {
  int id;
  String name;

  public boolean equals(Object o) {
    if (!(o instanceof MyClass))
      return false;
    MyClass other = (MyClass) o;
    if (other.id == this.id || other.name == this.name)
      return true;
    return false;
  }
}

come scrivo una funzione hashCode() per questa classe?e voglio evitare il caso banale qui di restituire una costante in questo modo:

public int hashCode() {
  return 1;
}
È stato utile?

Soluzione

Non penso che esista un hashcode non banale. Inoltre, il equals() viola il contratto generale come indicato nell'API --- è non transitivo :

(1,2) è uguale a (1,3)

(4,3) è uguale a (a,b)

Ma (c,d) non è non uguale a h(a,b) ≠ h(c,d).


Per completezza, vi presento Skeet - Niko proof =)

Reclama : L'hashcode deve essere la funzione banale costante.

Prova : lascia (a,d) e h(a,d) = h(a,b) = h(c,d) due oggetti con hashcode distinti, ovvero <=>. Considera l'oggetto <=>. Secondo la definizione del PO, <=> è uguale a <=> e <=> è uguale a <=>. Segue dal contratto hashcode che <=>; una contraddizione.

Altri suggerimenti

Ok, nel tuo scenario, ignorando i requisiti API per un secondo, non esiste una funzione hash non costante

Immagina che ci fosse una funzione hash che ha valori diversi per

(a, b), (a, c), b! = c, quindi hash (a, b)! = hash (a, c), anche se (a, b) = (a, c).

Allo stesso modo, (b, a) e (c, a) devono emettere lo stesso hashCode.

Chiamiamo la nostra funzione hash h. Troviamo:

h (x, y) = h (x, w) = h (v, w) forall x, y, v, w.

Quindi, l'unica funzione hash che fa quello che vuoi è costante.

Sono abbastanza sicuro che Zach abbia ragione: non esiste un hashcode non banale per farlo.

Pseudo-proof:

Considera due valori non uguali, X = (id1, nome1) e Y = (id2, nome2).

Ora considera Z = (id2, nome1). Questo è uguale sia a X che a Y, quindi deve avere lo stesso hashcode sia di X che di Y. Pertanto X e Y devono avere lo stesso hashcode, il che significa che tutti devono avere lo stesso hashcode.

C'è una ragione per cui ti sei imbattuto in una strana situazione: stai infrangendo la natura transitiva degli uguali. Il fatto che X.equals (Z) e Z.equals (Y) dovrebbe significa che X.equals (Y) - ma non è così. La tua definizione di uguaglianza non è adatta al normale contratto di uguaglianza.

Penso che non puoi. Il motivo è che il tuo equals() metodo non è transitivo.

Transitività significa per tre x, y, z non nulli se x.equals(y), y.equals(z), quindi x.equals(z). Nel tuo esempio, un oggetto x={id: 1, name: "ha"}, y={id: 1, name: "foo"}, z={id: 2, name: "bar"} ha questa proprietà (x.equals(y) and y.equals(z)). Tuttavia, false è f(x)==f(y). Ogni x==y metodo dovrebbe avere questa proprietà, consultare i documenti dell'API Java.

Torna alle funzioni di hashing: ogni funzione produce un'equivalenza definita da <=>. Ciò significa che se sei interessato al confronto dei valori della funzione e vuoi che ritorni vero se <=> (e possibilmente in altri casi), riceverai una relazione transitiva, il che significa che devi considerare almeno una chiusura transitiva di equivalenza degli oggetti. Nel tuo caso, la chiusura transitiva è la relazione banale (tutto è uguale a qualsiasi cosa). Ciò significa che non puoi distinguere oggetti diversi da nessuna funzione.

Hai intenzionalmente definito l'uguaglianza come quando gli ID sono uguali O i nomi sono uguali..L'"OR" non dovrebbe essere un "AND"?

Se intendevi "AND", il tuo hashcode dovrebbe essere calcolato utilizzando gli stessi campi o meno (ma non utilizzare mai campi non utilizzati da equals) dei campi equals().

Se intendevi "OR", il tuo hashcode non dovrebbe includere ID o nome nel calcolo del codice hash, il che non ha davvero senso.

EDIT: non ho letto attentamente la domanda.

-

Userò commons-lang jar.

XOR dovrebbe funzionare il hashCode dei membri. Come dovrebbero implementare correttamente hashCode () ed equals ().

Tuttavia, il tuo codice potrebbe non essere corretto se non proteggi il tuo codice hash. Una volta che è stato hash, non dovrebbe essere modificato. Dovrebbe essere impedito che accada.

public hashCode(){
   return new AssertionError();
}

o

 public class MyClass {
   final int id;
   final String name;
   // constructor
 }

o

public class MyClass {
   private int id;
   private String name;
   boolean hashed=false;
   public void setId(int value){
     if(hashed)throw new IllegalStateException();
     this.id=value;
   }
   public void setName(String value){
     if(hashed)throw new IllegalStateException();
     this.name=value;
   }
   // your equals() here
   public hashCode(){
     hashed=true;
     return new HashCodeBuilder().append(id).append(name).toHashCode();
   }
}

Dopo aver riletto la domanda.

È possibile completare automaticamente un altro campo quando uno di essi viene aggiornato.

-

EDIT: il mio codice potrebbe dire meglio del mio inglese.

void setName(String value){
  this.id=Lookup.IDbyName(value);
}
void setID(String value){
  this.name=Lookup.NamebyId(value);
}

MODIFICA 2:

Il codice sulla domanda potrebbe essere errato poiché restituirà sempre vero a meno che non sia stato impostato l'amplificatore id &; nome.

Se vuoi davvero un metodo che fa uguali parziali, crea la tua API che ha chiamato " partialEquals () " ;.

Il percorso più semplice è XOR per gli hashcode di ogni singolo campo. Questo ha una bruttezza minore in alcune situazioni (ad es. Nelle coordinate X, Y, provoca la situazione potenzialmente povera di avere hash uguali quando si capovolgono X e Y), ma nel complesso è piuttosto efficace. Modificare se necessario per ridurre le collisioni, se necessario per l'efficienza.

Che ne dici di questo

public override int GetHashCode()
{
    return (id.ToString() + name.ToString()).GetHashCode();
}

La funzione dovrebbe sempre restituire un " valido " hash ...

Modifica: ho appena notato che usi " o " non " e " : P Beh, dubito che ci sia una buona soluzione a questo problema ...

Che ne dici

public override int GetHashCode()
{
    return id.GetHashCode() ^ name.GetHashCode();
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top