2008-09-09 18 views
14

Il valore restituito di GetHashCode() è garantito come coerente assumendo che venga utilizzato lo stesso valore di stringa? (C#/ASP.NET)Posso dipendere dai valori di GetHashCode() per essere coerenti?

Ho caricato il mio codice su un server oggi e con mia sorpresa ho dovuto reindicizzare alcuni dati perché il mio server (win2008 64-bit) restituiva valori diversi rispetto al mio computer desktop.

risposta

29

Se non sbaglio, GetHashCode è coerente dato lo stesso valore, ma non è garantito per essere coerenti tra diverse versioni del quadro.

Dalla documentazione MSDN su String.GetHashCode():

Il comportamento di GetHashCode dipende dalla sua attuazione, che potrebbe passare da una versione del Common Language Runtime ad un altro. Un motivo per cui ciò potrebbe accadere è migliorare le prestazioni di GetHashCode.

+1

Conclusione: non persistere o trasmettere il risultato di 'GetHashCode()'. Usalo solo per lo scopo previsto: facilitare l'uso delle tabelle hash. –

0

Mi chiedo se ci sono differenze tra i sistemi operativi a 32-bit e 64-bit, perché sono certo sia il mio server e computer di casa sono in esecuzione la stessa versione di .NET

ero sempre stanco di usando GetHashCode(), potrebbe essere una buona idea per me semplicemente ricoprire il mio algoritmo di hash. Beh, almeno ho finito per scrivere una rapida pagina di re-index .aspx a causa di esso.

0

sono in esecuzione Win2008 x86 nel tuo desktop? Perché Win2008 include la versione 2.0.50727.1434, che è una versione aggiornata di 2.0 inclusa in Vista RTM.

0

Non è una risposta diretta alla sua domanda, che Jonas ha risposto bene, tuttavia questo può essere di aiuto, se siete preoccupati per test di uguaglianza in hash

Dai nostri test, a seconda di ciò che si sta richiedendo con codici hash, in C#, gli hashcode non devono essere univoci per le operazioni di uguaglianza. Ad esempio, considerare quanto segue:

Avevamo il requisito di sovraccaricare l'operatore di uguale, e quindi la funzione GetHashCode dei nostri oggetti non appena erano diventati volatili e senza stato, e di procurarsi direttamente dai dati, quindi in un unico punto di l'applicazione abbiamo bisogno di garantire che un oggetto potrebbe essere visto come uguale a un altro oggetto se è stato provenienti dagli stessi dati, non solo se era lo stesso riferimento. I nostri identificatori di dati univoci sono Guids.

L'operatore è uguale è stato facile per soddisfare come abbiamo appena controllato sul Guid del record (dopo aver controllato per nulla).

Sfortunatamente la dimensione dei dati HashCode (essendo un int) dipende dal sistema operativo, e sul nostro sistema a 32 bit, l'hashcode sarebbe 32 bit. Matematicamente, quando si sostituisce la funzione GetHashCode, è impossibile generare un codice hash univoco da un guid che è maggiore di 32 bit (osservarlo dal contrario, come si tradurrebbe un intero a 32 bit in un guid?).

Abbiamo quindi effettuato alcuni test in cui abbiamo preso il Guid come stringa e restituito il codice hash del Guid, che restituisce quasi sempre un identificativo univoco nei nostri test, ma non sempre.

Ciò che abbiamo notato, tuttavia, quando un oggetto si trova in un oggetto di raccolta hash (un hashtable, un dizionario, ecc.), Quando 2 oggetti non sono univoci ma i loro hashcode sono, l'hashcode viene utilizzato solo come prima opzione di ricerca, se sono utilizzati codici hash non univoci, l'operatore di uguaglianza viene sempre utilizzato come ripiego per determinare l'uguaglianza.

Come ho detto, questo può o non può essere rilevante per la tua situazione, ma se è un suggerimento utile.

UPDATE

Per dimostrare, abbiamo un Hashtable:

chiave: Object Un (HashCode 1), Valore oggetto A1

chiave: oggetto B (HashCode 1), Valore oggetto B1

chiave: Object C (hashCode 1), valore oggetto C1

chiave: oggetto D (hashCode 2), il valore Ob Ject D1

chiave: Object E (HashCode 3), oggetto valore E1

Quando io chiamo la tabella hash per l'oggetto con la chiave dell'oggetto A, verrà restituito l'oggetto A1 dopo 2 passaggi, un invito a hashcode 1, quindi un controllo di uguaglianza sull'oggetto chiave poiché non esiste una chiave univoca con l'hashcode 1

Quando chiamo l'hashtable per l'oggetto con la chiave dell'oggetto D, l'oggetto D1 verrà restituito dopo 1 passo , una ricerca hash

0

Ciò che abbiamo notato tuttavia, quando un oggetto si trova in un oggetto di raccolta hash (una tabella hash, un dizionario etc), quando 2 oggetti non sono univoci ma i loro codici hash sono, il codice hash viene utilizzato solo come una prima occhiata opzione se ci sono non univoco codici hash in uso, l'operatore di uguaglianza è sempre utilizzato come ripiego a uguaglianza detemine.

Questo è il modo in cui funzionano le ricerche hash, giusto? Ogni bucket contiene un elenco di elementi con lo stesso codice hash.

Quindi, per trovare l'elemento corretto in queste condizioni, viene eseguita una ricerca lineare utilizzando il confronto dell'uguaglianza dei valori.

E se l'implementazione dell'hashing raggiunge una buona distribuzione, questa ricerca non è necessaria, vale a dire un articolo per bucket.

La mia comprensione è corretta?

+0

Ben, dai nostri test, questo è vero. La seconda ricerca di uguaglianza viene eseguita solo come richiesto. Puoi testarlo da solo sovraccaricando ==,! =, Equals() e GetHashCode di un determinato oggetto. L'ho trovato molto interessante (ma sono un geek :)) – johnc

+0

(continua), quindi l'effetto "knock on" dei codici hash non univoci potrebbe essere una performance più lenta per eseguire il controllo di uguaglianza, ma nella nostra situazione in cui il valore non univoco è molto raro, è in gran parte insignificante – johnc

5

L'implementazione dipende dalla versione del framework ma dipende anche dallo architecture. L'implementazione di string.GetHashCode() è dfferent nelle versioni x86 e x64 del framework anche se hanno lo stesso numero di versione.

10

Ho avuto un problema simile in cui ho riempito una tabella di database con informazioni che dipendevano da String.GetHashCode (non è la migliore idea) e quando ho aggiornato il server a cui stavo lavorando x64 ho notato i valori che stavo ottenendo da Stringa.GetHashCode era incoerente con quello che era già nella tabella. La mia soluzione era usare la mia versione di GetHashCode che restituisce lo stesso valore di String.GetHashCode su un framework x86.

Ecco il codice, non dimenticate di compilare con "Consenti codice non sicuro":

/// <summary> 
    /// Similar to String.GetHashCode but returns the same as the x86 version of String.GetHashCode for x64 and x86 frameworks. 
    /// </summary> 
    /// <param name="s"></param> 
    /// <returns></returns> 
    public static unsafe int GetHashCode32(string s) 
    { 
     fixed (char* str = s.ToCharArray()) 
     { 
      char* chPtr = str; 
      int num = 0x15051505; 
      int num2 = num; 
      int* numPtr = (int*)chPtr; 
      for (int i = s.Length; i > 0; i -= 4) 
      { 
       num = (((num << 5) + num) + (num >> 0x1b))^numPtr[0]; 
       if (i <= 2) 
       { 
        break; 
       } 
       num2 = (((num2 << 5) + num2) + (num2 >> 0x1b))^numPtr[1]; 
       numPtr += 2; 
      } 
      return (num + (num2 * 0x5d588b65)); 
     } 
    } 
+1

Ho avuto lo stesso problema e ho portato la tua versione a un metodo sicuro. https://gist.github.com/gerriten/7542231#file-gethashcode32-net –

-1

avrei dovuto dire ... non si può fare affidamento su di esso. Ad esempio, se eseguo file1 attraverso il codice hash md5 di C# e copio nd, incollo lo stesso file in una nuova directory ... il codice hash risulta diverso anche se è lo stesso file. Ovviamente è la stessa versione .net, stesso tutto. L'unica cosa che è cambiata è stata la via.

1
/// <summary> 
    /// Default implementation of string.GetHashCode is not consistent on different platforms (x32/x64 which is our case) and frameworks. 
    /// FNV-1a - (Fowler/Noll/Vo) is a fast, consistent, non-cryptographic hash algorithm with good dispersion. (see http://isthe.com/chongo/tech/comp/fnv/#FNV-1a) 
    /// </summary> 
    private static int GetFNV1aHashCode(string str) 
    { 
     if (str == null) 
      return 0; 
     var length = str.Length; 
     // original FNV-1a has 32 bit offset_basis = 2166136261 but length gives a bit better dispersion (2%) for our case where all the strings are equal length, for example: "3EC0FFFF01ECD9C4001B01E2A707" 
     int hash = length; 
     for (int i = 0; i != length; ++i) 
      hash = (hash^str[i]) * 16777619; 
     return hash; 
    } 

Questa implementazione può essere più lenta di quella non registrata precedentemente pubblicata. Ma molto più semplice e sicuro.

Problemi correlati