2009-06-05 5 views
13

Ho un classe Address in C# che assomiglia a questo:Devo usare una concatenazione dei miei campi stringa come codice hash?

public class Address 
{    
    public string StreetAddress { get; set; } 
    public string RuralRoute { get; set; } 
    public string City { get; set; } 
    public string Province { get; set; } 
    public string Country { get; set; } 
    public string PostalCode { get; set; } 
} 

sto implementando l'uguaglianza e quindi ho bisogno di ignorare il codice hash. All'inizio avrei usato la formula hashcode di EJ ma poi ho pensato: questi sono tutti campi stringa, non posso semplicemente usare uno StringBuilder per concatenarli e restituire il codice hash da quella stringa?

Cioè:

var str = new StringBuilder(); 
str.Append(StreetAddress) 
    .Append(RuralRoute) 
    ... 

return str.ToString().GetHashCode(); 

Quali sono i vantaggi/svantaggi di questo? Perché non dovrei farlo?

+1

Per la vostra considerazione: non http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-systemobjectgethashcode – lance

risposta

14

Eviterei di farlo semplicemente perché crea una serie di stringhe inutilmente - anche se il punto di vista di Kosi2801 sul rendere semplici le collisioni è anche rilevante. (Ho il sospetto che non sarebbe in realtà creare molte collisioni, a causa della natura dei campi, ma ...)

vorrei andare per il "semplice e facile da ottenere" algoritmo ho già used in this answer (grazie per la ricerca di lance :) - e che è elencato in Java efficace, come hai detto tu. In questo caso si finirebbe come:

public int GetHashCode() 
{ 
    int hash = 17; 
    // Suitable nullity checks etc, of course :) 
    hash = hash * 23 + StreetAddress.GetHashCode(); 
    hash = hash * 23 + RuralRoute.GetHashCode(); 
    hash = hash * 23 + City.GetHashCode(); 
    hash = hash * 23 + Province.GetHashCode(); 
    hash = hash * 23 + Country.GetHashCode(); 
    hash = hash * 23 + PostalCode.GetHashCode(); 
    return hash; 
} 

Ovviamente non è sicuro. Se stai usando C# 3 si potrebbe prendere in considerazione un metodo di estensione:

public static int GetNullSafeHashCode<T>(this T value) where T : class 
{ 
    return value == null ? 1 : value.GetHashCode(); 
} 

quindi è possibile utilizzare:

public int GetHashCode() 
{ 
    int hash = 17; 
    // Suitable nullity checks etc, of course :) 
    hash = hash * 23 + StreetAddress.GetNullSafeHashCode(); 
    hash = hash * 23 + RuralRoute.GetNullSafeHashCode(); 
    hash = hash * 23 + City.GetNullSafeHashCode(); 
    hash = hash * 23 + Province.GetNullSafeHashCode(); 
    hash = hash * 23 + Country.GetNullSafeHashCode(); 
    hash = hash * 23 + PostalCode.GetNullSafeHashCode(); 
    return hash; 
} 

È potrebbe creare un programma di utilità metodo di matrice di parametri per rendere questo ancora più semplice :

public static int GetHashCode(params object[] values) 
{ 
    int hash = 17; 
    foreach (object value in values) 
    { 
     hash = hash * 23 + value.GetNullSafeHashCode(); 
    } 
    return hash; 
} 

e chiamarlo con:

public int GetHashCode() 
{ 
    return HashHelpers.GetHashCode(StreetAddress, RuralRoute, City, 
            Province, Country, PostalCode); 
} 

Nella maggior parte dei tipi ci sono le primitive coinvolte, in modo da eseguire la boxe un po 'inutilmente, ma in questo caso avresti solo riferimenti. Naturalmente, ci si finisce per creare una matrice inutilmente, ma sai quello che dicono di ottimizzazione prematura ...

+1

Un'altra soluzione è usare EqualityComparer . Default.GetHashCode (someValue). Questo è un meccanismo di hashing nullo ed è nel framework dal 2.0 – JaredPar

+0

Grazie, funziona perfettamente. C'è un piccolo errore nel metodo GetNullSafeHashCode(): manca "questo". – cdmckay

+0

Doh! Grazie mille. Fisso. –

5

Non farlo perché gli oggetti possono essere diversi anche se il codice hash è lo stesso.

Pensate

"StreetAddress" + "RuralRoute" + "City" 

vs

"Street" + "AddressRural" + "RouteCity" 

Entrambi avranno lo stesso codice hash, ma contenuti differenti nei campi.

+0

Questo è un buon punto, ho avuto anche considerato questo. Anche se sembra improbabile che si verifichi nella pratica. – cdmckay

+1

Non è facile risolverlo posizionando un delimitatore tra i campi? (Ad esempio '" StreetAddress "+"/delimiter/"+" RuralRoute "+"/delimiter/"+" City "' Potrebbe essere una variabile per evitare assegnazioni multiple in alcune lingue?) – simonra

0

Per questo genere di cose, si potrebbe desiderare di implementare IEqualityComparer<Address>:

public class Address : IEqualityComparer<Address> 
{   
    // 
    // member declarations 
    // 

    bool IEqualityComparer<Address>.Equals(Address x, Address y) 
    { 
     // implementation here 
    } 

    int IEqualityComparer<Address>.GetHashCode(Item obj) 
    { 
     // implementation here 
    } 
} 

È potrebbe anche implementare IComparable<Address> per ottenere l'ordine ...

-4
public string getfourDigitEncryptedText(string input) { 
    int hashCode = input.hashCode(); 
    string hstring = (new StringBuilder()).append(hashCode).append("").toString(); 
    string rev_hstring = (new StringBuilder(hstring)).reverse().toString(); 
    string parts[] = rev_hstring.trim().split(""); 
    int prefixint = 0; 
    for(int i = 1; i <= parts.length - 3; i++) 
     prefixint += integer.parseInt(parts[i]); 
    string prefixstr = "0"; 
    if((new integer(prefixint)).toString().length() < 2) 
     prefixstr = (new StringBuilder()).append((new integer(prefixint)).toString()).append("5").toString(); 
    else if((new integer(prefixint)).toString().length() > 2) 
     prefixstr = "79"; 
    else 
     prefixstr = (new integer(prefixint)).toString(); 
    string finalstr = (new StringBuilder()).append(prefixint).append(rev_hstring.substring(3, 5)).toString(); 
    return finalstr; 
} 
+0

Puoi fornire una spiegazione di cosa fa il tuo codice? – DeanOC

Problemi correlati