Ich habe eine Struktur in C #:
public struct UserInfo
{
public string str1
{
get;
set;
}
public string str2
{
get;
set;
}
}
Die einzige Regel ist das UserInfo(str1="AA", str2="BB").Equals(UserInfo(str1="BB", str2="AA"))
Wie überschreibe ich die GetHashCode-Funktion für diese Struktur?
Antworten:
MSDN :
Eine Hash-Funktion muss die folgenden Eigenschaften haben:
Die richtige Berücksichtigung ist:
return str1.GetHashCode() ^ str2.GetHashCode()
^
kann durch eine andere kommutative Operation ersetzt werdenquelle
Siehe Jon Skeets Antwort - binäre Operationen wie
^
sind nicht gut, sie erzeugen oft kollidierenden Hash!quelle
F(a,b) == F(b,a)
...public override int GetHashCode() { unchecked { return (str1 ?? String.Empty).GetHashCode() + (str2 ?? String.Empty).GetHashCode(); } }
Die Verwendung des Operators '+' ist möglicherweise besser als die Verwendung von '^', da Sie zwar explizit möchten, dass ('AA', 'BB') und ('BB', 'AA') explizit identisch sind, dies aber möglicherweise nicht möchten ( 'AA', 'AA') und ('BB', 'BB') müssen gleich sein (oder alle gleichen Paare).
Die Regel 'so schnell wie möglich' wird in dieser Lösung nicht vollständig eingehalten, da bei Nullen ein 'GetHashCode ()' für die leere Zeichenfolge ausgeführt wird, anstatt sofort eine bekannte Konstante zurückzugeben, aber auch ohne explizite Messung bin ich bereit eine Vermutung zu riskieren, dass der Unterschied nicht groß genug wäre, um sich Sorgen zu machen, wenn Sie nicht viele Nullen erwarten.
quelle
In der Regel besteht eine einfache Möglichkeit zum Generieren eines Hashcodes für eine Klasse darin, alle Datenfelder, die an der Generierung des Hashcodes beteiligt sein können, zu XOREN (wobei darauf zu achten ist, dass andere auf Null prüfen). Dies erfüllt auch die (künstliche?) Anforderung, dass die Hashcodes für UserInfo ("AA", "BB") und UserInfo ("BB", "AA") identisch sind.
Wenn Sie Annahmen über die Verwendung Ihrer Klasse treffen können, können Sie möglicherweise Ihre Hash-Funktion verbessern. Wenn beispielsweise str1 und str2 häufig gleich sind, ist XOR möglicherweise keine gute Wahl. Wenn jedoch str1 und str2 beispielsweise Vor- und Nachnamen darstellen, ist XOR wahrscheinlich eine gute Wahl.
Obwohl dies eindeutig kein reales Beispiel sein soll, kann darauf hingewiesen werden, dass: - dies wahrscheinlich ein schlechtes Beispiel für die Verwendung einer Struktur ist: Eine Struktur sollte normalerweise eine Wertesemantik haben, was nicht der Fall zu sein scheint der Fall hier. - Die Verwendung von Eigenschaften mit Setzern zum Generieren eines Hash-Codes ist ebenfalls problematisch.
quelle
Ein einfacher allgemeiner Weg ist dies:
return string.Format("{0}/{1}", str1, str2).GetHashCode();
Wenn Sie keine strengen Leistungsanforderungen haben, ist dies die einfachste, die ich mir vorstellen kann, und ich verwende diese Methode häufig, wenn ich einen zusammengesetzten Schlüssel benötige. Es behandelt die
null
Fälle einwandfrei und verursacht (m) keine Hash-Kollisionen (im Allgemeinen). Wenn Sie '/' in Ihren Zeichenfolgen erwarten, wählen Sie einfach ein anderes Trennzeichen, das Sie nicht erwarten.quelle
return $"{str1}/{str2}".GetHashCode();
. Siehe String InterpolationEquals
zu oft angerufen wird (ein genauer Vergleich ist häufig teurer).public override int GetHashCode() { unchecked { return(str1 != null ? str1.GetHashCode() : 0) ^ (str2 != null ? str2.GetHashCode() : 0); } }
quelle
In diesem Sinne schlägt ReSharper Folgendes vor:
public int GetHashCode() { unchecked { int hashCode; // String properties hashCode = (hashCode * 397) ^ (str1!= null ? str1.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (str2!= null ? str1.GetHashCode() : 0); // int properties hashCode = (hashCode * 397) ^ intProperty; return hashCode; } }
397 ist eine Primzahl von ausreichender Größe, um zu bewirken, dass die Ergebnisvariable überläuft und die Bits des Hash etwas mischt, wodurch eine bessere Verteilung der Hash-Codes bereitgestellt wird. Ansonsten gibt es in 397 nichts Besonderes, das es von anderen Primzahlen gleicher Größe unterscheidet.
quelle
Ach ja, wie Gary Shutler betonte:
return str1.GetHashCode() + str2.GetHashCode();
Kann überlaufen. Sie können versuchen, das Casting so lange durchzuführen, wie Artem es vorgeschlagen hat, oder Sie können die Anweisung mit dem nicht aktivierten Schlüsselwort umgeben:
return unchecked(str1.GetHashCode() + str2.GetHashCode());
quelle
Probieren Sie dieses aus:
(((long)str1.GetHashCode()) + ((long)str2.GetHashCode())).GetHashCode()
quelle
Viele Möglichkeiten. Z.B
return str1.GetHashCode() ^ str1.GetHashCode()
quelle
Vielleicht so etwas wie str1.GetHashCode () + str2.GetHashCode ()? oder (str1.GetHashCode () + str2.GetHashCode ()) / 2? Auf diese Weise wäre es das gleiche, unabhängig davon, ob str1 und str2 vertauscht werden ....
quelle
Sortieren Sie sie und verketten Sie sie dann:
quelle
Das Ergebnis von GetHashCode soll sein:
Wenn ich das bedenke, würde ich so etwas machen:
if (str1 == null) if (str2 == null) return 0; else return str2.GetHashCode(); else if (str2 == null) return str1.GetHashCode(); else return ((ulong)str1.GetHashCode() | ((ulong)str2.GetHashCode() << 32)).GetHashCode();
Bearbeiten: Die Nullen vergessen. Code behoben.
quelle
Zu kompliziert und vergisst Nullen usw. Dies wird für Dinge wie Bucketing verwendet, damit Sie mit so etwas davonkommen können
if (null != str1) { return str1.GetHashCode(); } if (null != str2) { return str2.GetHashCode(); } //Not sure what you would put here, some constant value will do return 0;
Dies wird durch die Annahme verzerrt, dass str1 in einem ungewöhnlich großen Anteil von Fällen wahrscheinlich nicht häufig vorkommt.
quelle