首先声明一下,这里的GetHashCode是Object.GetHashCode,是需要在对象中定义的函数。这个函数在对象被插入到字典Dictionary<TKey, TValue>或者HashSet<T>之类的哈希表中的时候会被调用,用于生成hash键值。关于哈希表:
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
HashCode的规范:
第一条是必须实现的,否则Dictionary这类数据结构无法正常使用;第二条则是尽量实现,如果实现得不好的话会影响实际使用时的存取性能。
因此,GetHashCode可以用于辅助快速判断两个Object是否相等,之所以是辅助是因为即使是不同的两个Object,也是有可能拥有同样的HashCode的。
直接使用默认的ValueType的GetHashCode实现容易造成哈希冲突,这样的Object在配合哈希表这类数据结构使用的时候会出现性能问题。
GetHashCode的高效定义方法
class Person:IEquatable<Person> { private int Age { get; } public string Name { get;}
public bool Equals(Person other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Age == other.Age && Name == other.Name; }
public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((Person) obj); }
public override int GetHashCode() { return HashCode.Combine<int, string>(Age, Name); } }
最多传入8个参数。在参数是简单的数据类型时(如Int)那么很高效。
class Person:IEquatable<Person> { public int Age { get; set; } public string Name { get;set; }
public Person(int age, string name) { Age = age; Name = name; }
public bool Equals(Person other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Age == other.Age && Name == other.Name; }
public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((Person) obj); }
public override int GetHashCode() { var hash = new HashCode(); hash.Add(Age); hash.Add(Name);
return hash.ToHashCode();; } }
利用元组
要求C#是7.0以上的版本。
class Person:IEquatable<Person> { public int Age { get; set; } public string Name { get;set; }
public Person(int age, string name) { Age = age; Name = name; }
public bool Equals(Person other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Age == other.Age && Name == other.Name; }
public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((Person) obj); }
public override int GetHashCode() { return (Age, Name).GetHashCode(); }
}
这个方法比较好的地方就在于是在栈上面完成的,不会触发GC。除了上面的还可以:
但是这样会触发GC,因为在堆上分配内存了。这是用到了C#的匿名类型来实现的。
基于两个素数、乘、加法
用两个不相通的素数,通过类似上面的乘法和加法计算就足够编写一个拥有较低Hash碰撞概率的算法。17和23只是例子,可以是随意的素数,越大越好。
基于异或
2166136261和16777619是FNV算法重的取值,上面这个算法模仿了FNV,所以沿用了FNV的取法。
更多相关技术内容咨询欢迎前往并持续关注六星社区了解详情。
想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!