2013-03-20 15 views
19

Ho studiato i metodi hashCode() in java e ho trovato quello strano per la classe String. Il codice sorgente è il seguente:Cosa c'è dietro il metodo hashCode() per String in Java?

public int hashCode() { 
    int h = hash; 
    if (h == 0 && value.length > 0) { 
     char val[] = value; 

     for (int i = 0; i < value.length; i++) { 
      h = 31 * h + val[i]; 
     } 
     hash = h; 
    } 
    return h; 
} 

Il codice stesso è abbastanza semplice. Ma mi chiedo quale sia la ragione per calcolare il codice hash in questo modo?
Perché scegliere 31?
Perché iniziare da 0 anziché value.length - 1?
Qualsiasi garanzia che ciò renderebbe hashcodes meno possibile collidere tra loro?

+2

Controllare questa risposta: http://stackoverflow.com/questions/113511/hash-code-implementation – NilsH

+3

E questo http: // StackOverflow .com/a/299748/305142 –

risposta

1

Sì, la probabilità di collisione hashcode è molto bassa, ad esempio in caso di stringa dipende dal valore di stringa. Se non stiamo creando alcuna stringa con un nuovo operatore, se la nuova stringa ha lo stesso valore già presente, allora il nuovo oggetto String non viene creato, si riferisce al vecchio valore dall'heap e in questo caso solo il valore di hashCode sii come previsto

Il contratto generale di hashCode è:

Ogni volta che viene richiamato sullo stesso oggetto più di una volta nel corso di un'esecuzione di un'applicazione Java, il metodo hashCode deve tornare sempre lo stesso numero intero, a condizione alcuna informazione utilizzata in pari i confronti sull'oggetto sono modificati. Questo numero intero non deve rimanere coerente da un'esecuzione di un'applicazione a un'altra esecuzione della stessa applicazione.

Da Java 1.2, la classe java.lang.String implementa il suo hashCode() utilizzando un algoritmo sum del prodotto sull'intero testo della stringa. [2] Dato un'istanza s della classe java.lang.String, per esempio, avrebbe un codice hash h (s) definita da

h(s)=s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] 

cui termini sono sommati utilizzando Java 32 bit int Inoltre, s [i] denota il carattere ith della stringa, e n è la lunghezza di s.

Per il vostro riferimento in Apache Harmony metodo hashCode è:

public int hashCode() { 
    if (hashCode == 0) { 
     int hash = 0, multiplier = 1; 
     for (int i = offset + count - 1; i >= offset; i--) { 
      hash += value[i] * multiplier; 
      int shifted = multiplier << 5; 
      multiplier = shifted - multiplier; 
     } 
     hashCode = hash; 
    } 
    return hashCode; 
} 
+2

Sembra curioso che fossero disposti a cambiare l'implementazione del codice hash in 1.2, ma da allora non sono stati disposti ad aggiungere qualcosa come 'hashCode = (hash == 0)? count + 1: hash; 'in modo da evitare di avere ripetute chiamate a' hashCode() 'impiega troppo tempo con certe stringhe. L'implementazione esistente non causa tali rallentamenti con molte stringhe, ma qualsiasi stringa che causi sempre il comportamento lento lo causerà sempre. – supercat

+0

@supercat: il tuo approccio funzionerebbe se c'è sempre una sola stringa con lo stesso contenuto. Java perlopiù strings, ma puoi ancora avere due copie con gli stessi caratteri. Il metodo hashCode dovrebbe essere coerente con equals(), quindi il tuo approccio non è valido. Ad esempio interrompere il comportamento di HashMap e HashSet (contiene, rimuovi, ecc. potrebbe non riuscire quando non dovrebbero). –

+1

@PeterBecker: Forse non ero chiaro cosa stavo proponendo? Ogni particolare sequenza di caratteri restituirebbe sempre lo stesso valore hash sotto la mia proposta; l'unico cambiamento sarebbe che le stringhe che sotto l'algoritmo esistente avrebbero un valore pari a zero avrebbero ceduto a un valore che dipendeva dal numero di caratteri nella sequenza (che sarebbe sempre lo stesso per qualsiasi sequenza particolare). Ciò che è problematico in quanto risulta non è l'hash set, ma piuttosto le istruzioni switch. Se una stringa in un'istruzione switch equivarrebbe a zero, tale ipotesi sarà cablata nel codice compilato. – supercat

Problemi correlati