Więc odpowiedź JaredPar nie jest złe ale mogłoby być lepiej na kilka sposobów. Przede wszystkim, the IEqualityComparer page mówi: "Zalecamy czerpanie z klasy EqualityComparer zamiast implementowania interfejsu IEqualityComparer."
Po drugie, implementacja GetHashCode ma być szybka. Służy do szybkiej eliminacji oczywiście różnych obiektów, co oczywiście byłoby stratą czasu na uruchomienie Equals. Tak więc GetHashCode powinien być znacznie szybszy niż faktycznie działający Equals.
trzecie, wracając suma tablicy bajtów jako JaredPar zrobił, jest bardzo prawdopodobne, aby produkować kolizji - jeżeli bajty są w innej kolejności, lub względne różnice znoszą się nawzajem, itp
Więc zamiast tego polecam takie rozwiązanie:
public class ByteArrayComparer : EqualityComparer<byte[]>
{
public override bool Equals(byte[] first, byte[] second)
{
if (first == null || second == null) {
// null == null returns true.
// non-null == null returns false.
return first == second;
}
if (ReferenceEquals(first, second)) {
return true;
}
if (first.Length != second.Length) {
return false;
}
// Linq extension method is based on IEnumerable, must evaluate every item.
return first.SequenceEqual(second);
}
public override int GetHashCode(byte[] obj)
{
if (obj == null) {
throw new ArgumentNullException("obj");
}
// quick and dirty, instantly identifies obviously different
// arrays as being different
return obj.Length;
}
}
Powyżej, wracając obj.Długość, to naprawdę szybkie i brudne, ale także podatne na powrót wielu kolizji. Myślę, że możemy zrobić lepiej.
Jeśli zamierzasz zbadać wszystkie bajty, coś takiego jest mniej podatne na kolizję niż prosta suma bajtów, jak w odpowiedzi JaredPar. Ale znowu, to bada wszystkie elementy, więc nie będzie lepiej, niż faktycznie działa Equals. Równie dobrze możesz po prostu zwrócić zero bezwarunkowo i zawsze wymuszać użycie równań.
Podkreślam: jest to lepsze niż zwrócenie kwoty, jak w odpowiedzi JaredPar. I zawsze zwracanie 0 jest lepsze niż to. A wracając obj.Length jest lepsze niż powrót 0.
// This is not recommended. Performance is too horrible.
public override int GetHashCode(byte[] obj)
{
// Inspired by fletcher checksum. Not fletcher.
if (obj == null) {
throw new ArgumentNullException("obj");
}
int sum = 0;
int sumOfSum = 0;
foreach (var val in obj) {
sum += val; // by default, addition is unchecked. does not throw OverflowException.
sumOfSum += sum;
}
return sum^sumOfSum;
}
Jeśli zdarzy się, że bajt [] tablice których używasz jako klucz były same skróty kryptograficzne, to można wykorzystać to założenie do zasiłku i po prostu zwróć pierwsze 4 bajty skonwertowane na int
. Prawdopodobnie działa zbyt dobrze, dla tablic bajtowych ogólnego przeznaczenia:
// This implementation works great if you assume the byte[] arrays
// are themselves cryptographic hashes. It probably works alright too,
// for general-purpose byte arrays.
public override int GetHashCode(byte[] obj)
{
if (obj == null) {
throw new ArgumentNullException("obj");
}
if (obj.Length >= 4) {
return BitConverter.ToInt32(obj, 0);
}
// Length occupies at most 2 bits. Might as well store them in the high order byte
int value = obj.Length;
foreach (var b in obj) {
value <<= 8;
value += b;
}
return value;
}
Użytkownik odpowiedział na własne pytanie ... – EricSchaefer
@Eric: ale bez zamieszczania tego pytania nie znałby znacznie lepszej opcji. :) –