15 Stimmen

Sollte ich eine Verkettung meiner Stringfelder als Hashcode verwenden?

Ich habe eine Adressklasse in C#, die wie folgt aussieht:

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; }
}

Ich implementiere die Gleichheit und muss daher den Hash-Code überschreiben. Zuerst wollte ich die Hashcode-Formel aus EJ verwenden, aber dann dachte ich: Dies sind alles String-Felder, kann ich nicht einfach nur einen StringBuilder verwenden, um sie zu verketten und den Hashcode aus diesem String zurückzugeben?

Das heißt:

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

return str.ToString().GetHashCode();

Was sind die Vor- und Nachteile? Warum sollte ich es nicht tun?

1 Stimmen

Zu Ihrer Information: stackoverflow.com/questions/263400/

16voto

Jon Skeet Punkte 1325502

Ich würde das vermeiden, einfach aus dem Grund, dass es ein Bündel von Zeichenfolgen sinnlos erstellt - obwohl Kosi2801's Punkt über die Herstellung von Kollisionen einfach ist auch relevant. (Ich vermute, es würde nicht eigentlich aufgrund der Beschaffenheit der Felder viele Kollisionen verursachen, aber...)

Ich würde mich für den Algorithmus "einfach und leicht richtig" entscheiden, den ich zuvor in dieser Antwort verwendet (danke fürs Nachschlagen, Lance :) - und die, wie Sie sagten, in Effective Java aufgeführt ist. In diesem Fall würde es enden als:

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;
}

Das ist natürlich nicht null-sicher. Wenn Sie C# 3 verwenden, sollten Sie vielleicht eine Erweiterungsmethode in Betracht ziehen:

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

Dann können Sie verwenden:

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;
}

Sie könnte ein Parameter-Array-Methoden-Dienstprogramm erstellen, um dies noch einfacher zu gestalten:

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

und rufen Sie es mit auf:

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

Bei den meisten Typen sind Primitive involviert, so dass das Boxing etwas unnötig wäre, aber in diesem Fall hätten Sie nur Referenzen. Natürlich würden Sie am Ende unnötigerweise ein Array erstellen, aber Sie wissen ja, was man über vorzeitige Optimierung sagt...

1 Stimmen

Eine andere Lösung ist die Verwendung von EqualityComparer<T>.Default.GetHashCode(someValue). Dies ist ein nullsicherer Hash-Mechanismus und ist im Framework seit 2.0 enthalten

0 Stimmen

Danke, das funktioniert perfekt. Es gibt einen kleinen Fehler in Ihrer GetNullSafeHashCode() Methode: es fehlt das "this".

0 Stimmen

Oje! Herzlichen Dank. Behoben.

5voto

Kosi2801 Punkte 20758

Tun Sie das nicht, denn die Objekte können unterschiedlich sein, auch wenn der Hashcode der gleiche ist.

Denken Sie an

"StreetAddress" + "RuralRoute" + "City"

gegen

"Street" + "AddressRural" + "RouteCity"

Beide haben den gleichen Hashcode, aber unterschiedliche Inhalte in den Feldern.

0 Stimmen

Das ist ein gutes Argument, das hatte ich gar nicht bedacht. Allerdings scheint es in der Praxis unwahrscheinlich zu sein.

1 Stimmen

Könnte man das nicht einfach lösen, indem man ein Trennzeichen zwischen die Felder setzt? (z.B. "StreetAddress" + "/delimiter/" + "RuralRoute" + "/delimiter/" + "City" . Könnte eine Variable sein, um Mehrfachzuweisungen in einigen Sprachen zu vermeiden? )

0voto

Keltex Punkte 25852

Für diese Art von Dingen sollten Sie vielleicht Folgendes implementieren 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
    }
}

Sie könnten auch Folgendes implementieren IComparable<Address> um die Bestellung zu erhalten...

-4voto

SARKAR LALTU Punkte 1
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 Stimmen

Können Sie erläutern, was Ihr Code bewirkt?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X