Domanda

In Java, ho una sottoclasse Vertex della classe Java3D Point3f . Ora Point3f calcola equals () in base ai valori delle sue coordinate, ma per la mia classe Vertex voglio essere più rigoroso: solo due vertici uguale se sono lo stesso oggetto. Fin qui tutto bene:

class Vertex extends Point3f {

    // ...

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

So che questo viola il contratto di equals () , ma dal momento che confronterò solo i vertici con altri vertici questo non è un problema.

Ora, per poter mettere i vertici in un HashMap , il metodo hashCode () deve restituire risultati coerenti con equals () . Attualmente lo fa, ma probabilmente basa il suo valore di ritorno sui campi del Point3f , e quindi genererà collisioni hash per diversi oggetti Vertex con le stesse coordinate.

Quindi vorrei basare hashCode () sull'indirizzo dell'oggetto, invece di calcolarlo dai campi di Vertex . So che la classe Object fa questo, ma non posso chiamare il suo metodo hashCode () perché Point3f lo sovrascrive.

Quindi, in realtà la mia domanda è duplice:

  • Dovrei anche desiderare un uguale a () ?
  • In caso affermativo, come posso ottenere l'indirizzo dell'oggetto da cui calcolare il codice hash?

Modifica: ho appena pensato a qualcosa ... Potrei generare un valore int casuale sulla creazione di oggetti, e usarlo per il codice hash. È una buona idea? Perché (no)?

È stato utile?

Soluzione

Usa System.identityHashCode () o usa IdentityHashMap.

Altri suggerimenti

System.identityHashCode () restituisce lo stesso codice hash per l'oggetto dato che verrebbe restituito dal metodo predefinito hashCode () , indipendentemente dal fatto che la classe dell'oggetto data sostituisce hashCode () .

Utilizzi un delegato anche se questa rispondi è probabilmente migliore.


class Vertex extends Point3f{
   private final Object equalsDelegate = new Object();
   public boolean equals(Object vertex){
      if(vertex instanceof Vertex){
         return this.equalsDelegate.equals(((Vertex)vertex).equalsDelegate);
      }
      else{
         return super.equals(vertex);
      }
   }
   public int hashCode(){
      return this.equalsDelegate.hashCode();
   }
}

Solo FYI, il tuo metodo uguale non viola il contratto uguale (per il contratto dell'oggetto base che è) ... questo è fondamentalmente il metodo uguale per il metodo oggetto base, quindi se vuoi che l'identità sia uguale al posto del vertice uguale , va bene.

Per quanto riguarda il codice hash, non è necessario modificarlo, sebbene la risposta accettata sia una buona opzione e sarà molto più efficiente se la tabella hash contiene molte chiavi di vertice con gli stessi valori.

Il motivo per cui non è necessario modificarlo è perché è del tutto corretto che il codice hash restituisca lo stesso valore per oggetti uguali restituisce false ... è anche un codice hash valido che restituisce solo 0 tutti tempo per OGNI istanza. Se questo è efficace per le tabelle hash è un problema completamente diverso ... otterrai molte più collisioni se molti dei tuoi oggetti hanno lo stesso codice hash (che può essere il caso se hai lasciato il codice hash da solo e hai avuto molti vertici con gli stessi valori).

Per favore, non accettarlo come risposta, ovviamente (quello che hai scelto è molto più pratico), volevo solo darti un po 'più di informazioni di base sui codici hash ed è uguale ;-)

Perché vuoi sovrascrivere hashCode () in primo luogo? Ti piacerebbe farlo se vuoi lavorare con qualche altra definizione di uguaglianza. Ad esempio

classe pubblica A {   int id;

booleano pubblico uguale a (A altro) {return other.id == id}   public int hashCode () {return id;}

} dove vuoi essere chiaro che se gli ID sono uguali, allora gli oggetti sono uguali e si sovrascrive l'hashcode in modo da non poterlo fare:

HashSet hash = new HashSet (); hash.add (nuovo A (1)); hash.add (nuovo A (1)); e ottieni 2 identici (dal punto di vista della tua definizione di uguaglianza) A. Il comportamento corretto sarebbe quindi che avresti solo 1 oggetto nell'hash, la seconda scrittura avrebbe sovrascritto.

Dal momento che non si utilizza uguale a un confronto logico, ma fisico (ovvero è lo stesso oggetto), l'unico modo per garantire che l'hashcode restituirà un valore univoco, è implementare una propria variante suggerimento. Invece di generare un numero casuale, usa UUID per generare un valore univoco effettivo per ciascun oggetto.

System.identityHashCode () funzionerà, la maggior parte del tempo, ma non è garantito poiché il metodo Object.hashCode () non è non garantito per restituire un unico valore per ogni oggetto. Ho visto accadere il caso marginale, e probabilmente dipenderà dall'implementazione della VM, che non è qualcosa su cui vorrai dipendere dal tuo codice.

Estratto dal javadocs per Object.hashCode (): Per quanto ragionevolmente pratico, il metodo hashCode definito dalla classe Object restituisce interi distinti per oggetti distinti. (Questo viene in genere implementato convertendo l'indirizzo interno dell'oggetto in un numero intero, ma questa tecnica di implementazione non è richiesta dal linguaggio di programmazione JavaTM.)

Il problema che questo affronta, è il caso in cui due oggetti punto separati si sovrascrivono quando vengono inseriti nella hashmap perché entrambi hanno lo stesso hash. Poiché non esiste alcun uguale logico, con l'override di hashCode () associato, il metodo identityHashCode può effettivamente causare questo scenario. Laddove il caso logico sostituisca solo le voci hash per lo stesso punto logico, l'utilizzo dell'hash basato sul sistema può causare che si verifichi con due oggetti qualsiasi, l'uguaglianza (e persino la classe) non è più un fattore.

La funzione hashCode () è ereditata da Object e funziona esattamente come previsto (a livello di oggetto, non a livello di coordinate). Non dovrebbe essere necessario modificarlo.

Per quanto riguarda il tuo metodo uguale, non c'è motivo di usarlo, dal momento che puoi semplicemente fare obj1 == obj2 nel tuo codice invece di usare uguale, poiché è pensato per l'ordinamento e simili, dove il confronto delle coordinate rende molto più senso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top