Таким образом, ответ JaredPar не является плохо но это может быть лучше несколькими способами. Прежде всего, the IEqualityComparer page говорит: «Мы рекомендуем, чтобы вы вышли из класса EqualityComparer вместо реализации интерфейса IEqualityComparer».
Во-вторых, реализация GetHashCode должна быть быстро. Он используется для быстрого устранения явно разных объектов, что, очевидно, будет пустой тратой времени для запуска Equals on. Таким образом, GetHashCode должен быть намного быстрее, чем на самом деле работает Equals.
В-третьих, возвращение суммы массива байтов, как JaredPar сделал, весьма вероятно, чтобы произвести столкновения - если байты в другом порядке, или относительные различия компенсируют друг друга, и т.д.
Я рекомендовал бы такое решение вместо этого:
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;
}
}
Вверху, возвращая объект.Длина, действительно быстро и грязно, но также склонны возвращать много столкновений. Я думаю, мы сможем сделать лучше.
Если вы собираетесь исследовать все байты, что-то вроде этого меньше, чем простая сумма байтов, как в ответе JaredPar. Но опять же, это исследует все элементы, поэтому он не будет работать лучше, чем фактически работает Equals. Вы могли бы просто вернуть 0 безоговорочно и всегда принудительно использовать Equals.
Подчеркнем: это лучше, чем возврат суммы, как в ответе ДжаредПар. И всегда возвращение 0 лучше, чем это. И возвращение obj.Length лучше, чем возвращение 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;
}
Если вы не знаете, что байты [] массивы, которые вы используете в качестве ключа были сами криптографическими хэш, то вы можете использовать это предположение в вашу пользу , и просто вернуть первые 4 байта, преобразованные в int
. Это, вероятно, работает нормально тоже, для общего назначения байтовых массивов:
// 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;
}
Вы ответили на свой вопрос ... – EricSchaefer
@ Эрик: но, не публикуя этот вопрос, он не знал бы гораздо лучшего варианта. :) –