Во-первых, еще раз проверьте, что вам вообще нужен хеш-код.Собираетесь ли вы поместить эти списки в структуру с хэш-отображением (например, словарь, хэш-набор и т. Д.)?Если нет, то забудьте об этом.
Теперь, предположив, что вы имеете в виду, что EnumerableObject уже переопределяет Equals(object)
(и, следовательно, мы надеемся, что по какой-то причине также реализует IEquatable<EnumerableObject>
), тогда это действительно необходимо.Вы хотите сбалансировать скорость и распределение битов.
Хорошая отправная точка - это тип mult + add или shift + xor:
public override int GetHashCode()
{
int res = 0x2D2816FE;
foreach(var item in this)
{
res = res * 31 + (item == null ? 0 : item.GetHashCode());
}
return res;
}
(Предполагается, что вы используете item.Equals() для сравнения равенства последовательностей, если вы используете equals IEqualityComparer, вам нужно вызвать его хеш-код).
Оттуда мы можем оптимизировать.
Если пустые элементы запрещены, удалите нулевую проверку (будьте осторожны, это вызовет сброс кода, если он когда-либо найдет ноль).
Если распространены очень большие списки, нам нужно уменьшить количество проверенных номеров, стараясь при этом не давать результатав большом количестве столкновений.Сравните следующие различные реализации:
public override int GetHashCode()
{
int res = 0x2D2816FE;
int max = Math.Min(Count, 16);
for(int i = 0, i != max; ++i)
{
var item = this[i];
res = res * 31 + (item == null ? 0 : item.GetHashCode());
}
return res;
}
public override int GetHashCode()
{
int res = 0x2D2816FE;
int min = Math.Max(-1, Count - 16);
for(int i = Count -1, i != min; --i)
{
var item = this[i];
res = res * 31 + (item == null ? 0 : item.GetHashCode());
}
return res;
}
public override int GetHashCode()
{
int res = 0x2D2816FE;
int step = Count / 16 + 1;
for(int i = 0, i < Count; i += step)
{
var item = this[i];
res = res * 31 + (item == null ? 0 : item.GetHashCode());
}
return res;
}
Каждая из них ограничивает общее количество проверенных элементов, что ускоряет выполнение, но рискует снизить качество хэшей.Какой (если таковой имеется) лучше всего зависит от того, являются ли более вероятными коллекции с одинаковым началом или одинаковым концом.
Изменение числа 16 выше регулирует баланс;чем меньше, тем быстрее, но чем выше, тем лучше качество хеша с меньшим риском коллизий хешей.
Редактировать: И теперь вы можете использовать мою реализацию SpookyHash v. 2 :
public override int GetHashCode()
{
var hasher = new SpookyHash();//use methods with seeds if you need to prevent HashDos
foreach(var item in this)
hasher.Update(item.GetHashCode());//or relevant feeds of item, etc.
return hasher.Final().GetHashCode();
}
Это создаст намного лучшее распределение, чем mult + add или shift + xor, но при этом будет особенно быстрым (особенно в 64-битных процессах, поскольку алгоритм оптимизирован для этого, хотя он также хорошо работает и в 32-битных системах).