我的问题可能与 Object.GetHashCode() 的 Default implementation重复,但我再次询问,因为我不理解该问题的公认答案。
首先,我对上一个问题的已接受答案有三个问题,其中引用了一些文档,如下所示:
“但是,由于这个索引可以在垃圾回收期间回收对象后重复使用,因此有可能为两个不同的对象获取相同的哈希码。”
这是真的?在我看来,两个对象不会有相同的哈希码,因为一个对象的代码在对象被垃圾收集(即不再存在)之前不会被重用。
“此外,表示相同值的两个对象只有当它们是完全相同的对象时才具有相同的哈希码。”
这是一个问题吗?例如,我想将一些数据与 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 数据成员的大小更大)。
我的实现和默认实现都是线性扩展的。
相比之下,作为一个健全的检查,愚蠢的实现开始很糟糕并且扩展得更糟。