Мой вопрос может дублировать реализацию по умолчанию для Object.GetHashCode (), но я спрашиваю снова, потому что я не понял принятый ответ на Вон тот.
Для начала у меня есть три вопроса о принятом ответе на предыдущий вопрос, который цитирует некоторую документацию, а именно:
«Однако, поскольку этот индекс можно повторно использовать после того, как объект будет восстановлен во время сборки мусора, можно получить один и тот же хэш-код для двух разных объектов».
Это правда? Мне кажется, что у двух объектов не будет одного и того же хэш-кода, потому что код объекта не используется повторно, пока объект не будет собран в мусор (т.е. больше не существует).
«Кроме того, два объекта, которые представляют одно и то же значение, имеют одинаковый хэш-код, только если они являются одними и теми же объектами».
Это проблема? Например, я хочу связать некоторые данные с каждым экземпляром узла в дереве DOM. Для этого «узлы» должны иметь идентификатор или хэш-код, чтобы я мог использовать их в качестве ключей в словаре данных. Разве не хэш-код, который идентифицирует, является ли он «одним и тем же объектом», то есть «ссылочным равенством, а не« равенством значений », то я хочу?
«Эта реализация не особенно полезна для хеширования; поэтому производные классы должны переопределять GetHashCode»
Это правда? Если он не подходит для хеширования, то что, если он для чего-нибудь пригоден, и почему он вообще определен как метод Object?
Мой последний (и, возможно, самый важный для меня) вопрос: если я должен изобрести / переопределить реализацию GetHashCode () для произвольного типа, который имеет семантику «ссылочного равенства», это следующая разумная и хорошая реализация:
class SomeType
{
//create a new value for each instance
static int s_allocated = 0;
//value associated with this instance
int m_allocated;
//more instance data
... plus other data members ...
//constructor
SomeType()
{
allocated = ++s_allocated;
}
//override GetHashCode
public override int GetHashCode()
{
return m_allocated;
}
}
Изменить
К вашему сведению, я протестировал это, используя следующий код:
class TestGetHash
{
//default implementation
class First
{
int m_x;
}
//my implementation
class Second
{
static int s_allocated = 0;
int m_allocated;
int m_x;
public Second()
{
m_allocated = ++s_allocated;
}
public override int GetHashCode()
{
return m_allocated;
}
}
//stupid worst-case implementation
class Third
{
int m_x;
public override int GetHashCode()
{
return 0;
}
}
internal static void test()
{
testT<First>(100, 1000);
testT<First>(1000, 100);
testT<Second>(100, 1000);
testT<Second>(1000, 100);
testT<Third>(100, 100);
testT<Third>(1000, 10);
}
static void testT<T>(int objects, int iterations)
where T : new()
{
System.Diagnostics.Stopwatch stopWatch =
System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < iterations; ++i)
{
Dictionary<T, object> dictionary = new Dictionary<T, object>();
for (int j = 0; j < objects; ++j)
{
T t = new T();
dictionary.Add(t, null);
}
for (int k = 0; k < 100; ++k)
{
foreach (T t in dictionary.Keys)
{
object o = dictionary[t];
}
}
}
stopWatch.Stop();
string stopwatchMessage = string.Format(
"Stopwatch: {0} type, {1} objects, {2} iterations, {3} msec",
typeof(T).Name, objects, iterations,
stopWatch.ElapsedMilliseconds);
System.Console.WriteLine(stopwatchMessage);
}
}
На моей машине результаты / вывод следующие:
First type, 100 objects, 1000 iterations, 2072 msec
First type, 1000 objects, 100 iterations, 2098 msec
Second type, 100 objects, 1000 iterations, 1300 msec
Second type, 1000 objects, 100 iterations, 1319 msec
Third type, 100 objects, 100 iterations, 1487 msec
Third type, 1000 objects, 10 iterations, 13754 msec
Моя реализация занимает половину времени по сравнению с реализацией по умолчанию (но мой тип больше на размер моего члена данных m_allocated).
Моя реализация и реализация по умолчанию масштабируются линейно.
Для сравнения и в качестве проверки работоспособности глупая реализация плохо запускается и еще хуже масштабируется.