通过搜索msdn c#文档和堆栈溢出,我得到了明确的印象:Dictionary<T,T>
应该使用GetHashCode()
来检查密钥唯一性并进行查找。
Dictionary泛型类提供从一组键到一组值的映射。字典的每个添加都包含一个值及其关联的键。通过使用其键来检索值非常快,接近于O(1),因为Dictionary类被实现为哈希表。 ... 检索速度取决于为TKey指定类型的哈希算法的质量。
我使用mono(在Unity3D中),在得到一些奇怪的结果后,我进行了这个实验:
public class DictionaryTest
{
public static void TestKeyUniqueness()
{
//Test a dictionary of type1
Dictionary<KeyType1, string> dictionaryType1 = new Dictionary<KeyType1, string>();
dictionaryType1[new KeyType1(1)] = "Val1";
if(dictionaryType1.ContainsKey(new KeyType1(1)))
{
Debug.Log ("Key in dicType1 was already present"); //This line does NOT print
}
//Test a dictionary of type1
Dictionary<KeyType2, string> dictionaryType2 = new Dictionary<KeyType2, string>();
dictionaryType2[new KeyType2(1)] = "Val1";
if(dictionaryType2.ContainsKey(new KeyType2(1)))
{
Debug.Log ("Key in dicType2 was already present"); // Only this line prints
}
}
}
//This type implements only GetHashCode()
public class KeyType1
{
private int var1;
public KeyType1(int v1)
{
var1 = v1;
}
public override int GetHashCode ()
{
return var1;
}
}
//This type implements both GetHashCode() and Equals(obj), where Equals uses the hashcode.
public class KeyType2
{
private int var1;
public KeyType2(int v1)
{
var1 = v1;
}
public override int GetHashCode ()
{
return var1;
}
public override bool Equals (object obj)
{
return GetHashCode() == obj.GetHashCode();
}
}
只有当使用类型KeyType2
时才认为密钥相等。对我来说,这表明Dictionary使用Equals(obj) - 而不是GetHashCode()。
有人可以重现这一点,并帮我解释其含义是什么?在单声道中是不正确的实现?或者我误解了什么。
答案 0 :(得分:2)
我得到了词典应该使用的清晰印象 .GetHashCode()用于检查密钥唯一性
是什么让你这么想的? GetHashCode
不会返回唯一值。
MSDN清楚地说:
Dictionary需要一个相等的实现 确定键是否相等。您可以指定实现 IEqualityComparer泛型接口使用的构造函数 接受比较器参数;如果您没有指定实现, 默认的通用相等比较器EqualityComparer.Default是 用过的。如果类型TKey实现System.IEquatable泛型 接口,默认的相等比较器使用该实现。
答案 1 :(得分:1)
这样做:
public override bool Equals (object obj)
{
return GetHashCode() == obj.GetHashCode();
}
在一般情况下是错误的,因为您可能最终得到KeyType2
个等于的实例,StringBuilder
,SomeOtherClass
,AnythingYouCanImagine
和什么不是实例。
你完全应该这样做:
public override bool Equals (object obj)
{
if (obj is KeyType2) {
return (obj as KeyType2).var1 == this.var1;
} else
return false;
}
当您尝试覆盖Equals
并且固有GetHashCode
时,您必须确保此顺序中的以下几点(给定MyObject类)(您正在执行其他操作)方式):
1)MyObject
的2个实例何时相等?说你有:
public class MyObject {
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
public DateTime TimeWhenIBroughtThisInstanceFromTheDatabase { get; set; }
}
您在某个数据库中有1条记录需要映射到此类的实例。
并且您制定了从数据库中读取记录的时间的约定
在TimeWhenIBroughtThisInstanceFromTheDatabase
:
MyObject obj1 = DbHelper.ReadFromDatabase( ...some params...);
// you do that at 14:05 and thusly the TimeWhenIBroughtThisInstanceFromTheDatabase
// will be assigned accordingly
// later.. at 14:07 you read the same record into a different instance of MyClass
MyObject obj2 = DbHelper.ReadFromDatabase( ...some params...);
// (the same)
// At 14:09 you ask yourself if the 2 instances are the same
bool theyAre = obj1.Equals(obj2)
您希望结果为 true 吗?我会说你做的。 因此,Equals的覆盖应该是这样的:
public class MyObject {
...
public override bool Equals(object obj) {
if (obj is MyObject) {
var that = obj as MyObject;
return (this.Name == that.Name) &&
(this.Address == that.Address) &&
(this.Age == that.Age);
// without the syntactically possible but logically challenged:
// && (this.TimeWhenIBroughtThisInstanceFromTheDatabase ==
// that.TimeWhenIBroughtThisInstanceFromTheDatabase)
} else
return false;
}
...
}
2)确保每当2个实例相等时(如您实施的Equals方法所示) 他们的GetHashCode结果将是identitcal。
int hash1 = obj1.GetHashCode();
int hash2 = obj2.GetHashCode();
bool theseMustBeAlso = hash1 == hash2;
最简单的方法是(在示例场景中):
public class MyObject {
...
public override int GetHashCode() {
int result;
result = ((this.Name != null) ? this.Name.GetHashCode() : 0) ^
((this.Address != null) ? this.Address.GetHashCode() : 0) ^
this.Age.GetHashCode();
// without the syntactically possible but logically challenged:
// ^ this.TimeWhenIBroughtThisInstanceFromTheDatabase.GetHashCode()
}
...
}
请注意:
- 字符串可以为空,.GetHashCode()
可能会因NullReferenceException
而失败。
- 我用了^(XOR)。只要遵守黄金法则(第2号),您就可以使用任何您想要的东西。
- x ^ 0 == x(对于任何x)