Perché dovrei aver bisogno di "equals" se ho già "hashcode"?

1

Ho una domanda sul perché abbiamo bisogno di uguali se abbiamo hashcode.

Il mio primo tentativo è stato la risposta perché collisione. Ma abbiamo corretto il punto di partenza con l'ipotesi che non abbiamo molti oggetti quindi non c'è nessuna collisione.

Il mio secondo tentativo è stata la risposta a causa della velocità. Ma ho anche avuto la risposta che esiste una differenza concettuale tra hashcode ed equals.

Quindi ho letto molti post, il documento java e non riesco a trovare la risposta. Mi manca qualcosa?

    
posta Eugen Martynov 14.01.2016 - 21:48
fonte

3 risposte

6

Per dirla chiaramente: servono a scopi diversi.

  • Equals serve per testare l'uguaglianza.
  • Hashcode è al fine di produrre un int (speriamo ben distribuito)

se il tuo hashcode è univoco , potresti anche usarlo per l'uguaglianza. Tuttavia, per molti tipi di oggetti, produrre un hash così unico è chiaramente impossibile.

Immagina un oggetto contenente due ints a e b . Non esiste alcun modo per produrre un hash int univoco per tale oggetto. Quindi hai ancora bisogno di uguaglianza per confrontarli.

Come per il comparatore, di solito è una cattiva idea usare l'output come hash perché il risultato non è ben distribuito, con il risultato di molte collisioni quando usato in mappe di hash o strutture di dati simili.

    
risposta data 14.01.2016 - 22:22
fonte
5

Per motivi di praticità, hashCode() restituisce un int firmato. 2 32 valori possibili. Uno potrebbe banalmente essere un long invece di un int e mi farebbe cambiare leggermente il resto di questa risposta.

L'essenza di tutto questo è il principio del pigeonhole . Se hai m elementi e provi a metterli in n box e m > n , allora avrai due elementi che vanno nella stessa casella.

Osserviamo hashCode per Long - che può avere 2 64 valori. Dal 2 64 > 2 32 avrai due valori con lo stesso valore.

public int hashCode() {
    return (int)(value ^ (value >>> 32));
}

Quindi, lascia scrivere un codice rapido.

System.out.println(Long.valueOf(0).hashCode());
System.out.println(Long.valueOf(-1).hashCode());
System.out.println(Long.valueOf(0).equals(Long.valueOf(-1)));
System.out.println(Long.valueOf(0).hashCode() == Long.valueOf(-1).hashCode());

Stampa:

0
0
false
true

E ce l'abbiamo. Due numeri Uno di questi è 0 e l'altro è -1 che ha lo stesso codice hash, ma non lo stesso numero.

Il valore esadecimale di -1 è 0xffffffffffffffff che quando fai un 0xffffffff ^ 0xffffffff ottieni 0

Ma aspetta! Cosa succede se abbiamo usato long per l'hashCode invece!

Come ho detto, è solo un problema leggermente diverso. Dovrei iniziare a scavare in un'altra classe che ha la possibilità di avere più valori di hashCode, come BigInteger o Stringa o Inet6Address (2 128 valori possibili ). Tutto ciò che fa è rendere il calcolo dell'hashCode un po 'più difficile da fare proprio qui (quelli sono più lunghi di una linea con operazioni a due bit). Non cambia il fatto che hashCode, essendo un valore di dimensione fissa, è soggetto al principio del pigeonhole.

    
risposta data 14.01.2016 - 23:09
fonte
2

Diciamo che hai un codice hash a 64 bit. Esistono 2 ^ 64 possibili codici hash. Ora diciamo che sei stringhe di hashing. Ci sono molti, molti più di 2 ^ 64 stringhe. Ci sono solo 2 ^ 64 stringhe di otto caratteri a 8 bit. Ci sono 256 stringhe in più di nove caratteri a 8 bit e ancora più stringhe di 10, 11 o più caratteri. Ovviamente ci saranno molte, molte stringhe di 9 caratteri e anche più lunghe con lo stesso codice hash.

Quindi non puoi fare affidamento solo sul codice hash. Uguali codici hash non garantiscono che gli oggetti siano identici.

    
risposta data 14.01.2016 - 22:36
fonte

Leggi altre domande sui tag