1、单个数据字段GetHashCode和Equals方法重写实现
计算与Int32类型具有相同或较小范围的数值的哈希码的最简单方法之一就是简单地返回该值。以下示例显示了这种Number结构的实现。
using System;
public struct Number
{
private int n;
public Number(int value)
{
n = value;
}
public int Value
{
get { return n; }
}
public override bool Equals(Object obj)
{
if (obj == null || ! (obj is Number))
return false;
else
return n == ((Number) obj).n;
}
public override int GetHashCode()
{
return n;
}
public override string ToString()
{
return n.ToString();
}
}
public class Example
{
public static void Main()
{
Random rnd = new Random();
for (int ctr = 0; ctr <= 9; ctr++) {
int randomN = rnd.Next(Int32.MinValue, Int32.MaxValue);
Number n = new Number(randomN);
Console.WriteLine("n = {0,12}, hash code = {1,12}", n, n.GetHashCode());
}
}
}
// The example displays output like the following:
// n = -634398368, hash code = -634398368
// n = 2136747730, hash code = 2136747730
// n = -1973417279, hash code = -1973417279
// n = 1101478715, hash code = 1101478715
// n = 2078057429, hash code = 2078057429
// n = -334489950, hash code = -334489950
// n = -68958230, hash code = -68958230
// n = -379951485, hash code = -379951485
// n = -31553685, hash code = -31553685
// n = 2105429592, hash code = 2105429592
2、多个数据字段GetHashCode和Equals方法重写实现
一个类型具有多个数据字段,这些数据字段可以参与生成哈希码。生成哈希码的一种方法是使用XOR (eXclusive OR)操作来组合这些字段,如以下示例所示。
using System;
// A type that represents a 2-D point.
public struct Point
{
private int x;
private int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public override bool Equals(Object obj)
{
if (! (obj is Point)) return false;
Point p = (Point) obj;
return x == p.x & y == p.y;
}
public override int GetHashCode()
{
return x ^ y;
}
}
public class Example
{
public static void Main()
{
Point pt = new Point(5, 8);
Console.WriteLine(pt.GetHashCode());
pt = new Point(8, 5);
Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 13
// 13
3、数据字段顺序不同GetHashCode和Equals方法重写实现
(n1,n2)和(n2,n1)返回相同的哈希码,因此可能会产生比期望更多的冲突。有许多解决方案可用,因此这些情况下的哈希码不相同。一种是返回Tuple反映每个字段顺序的对象的哈希码。以下示例显示了使用Tuple <T1,T2>类的可能实现。但是请注意,实例化Tuple对象的性能开销可能会严重影响在哈希表中存储大量对象的应用程序的整体性能。
using System;
public struct Point
{
private int x;
private int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public override bool Equals(Object obj)
{
if (!(obj is Point)) return false;
Point p = (Point) obj;
return x == p.x & y == p.y;
}
public override int GetHashCode()
{
return Tuple.Create(x, y).GetHashCode();
}
}
public class Example
{
public static void Main()
{
Point pt = new Point(5, 8);
Console.WriteLine(pt.GetHashCode());
pt = new Point(8, 5);
Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 173
// 269
或者
using System;
public struct Point
{
private int x;
private int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public override bool Equals(Object obj)
{
if (!(obj is Point)) return false;
Point p = (Point) obj;
return x == p.x & y == p.y;
}
public override int GetHashCode()
{
return ShiftAndWrap(x.GetHashCode(), 2) ^ y.GetHashCode();
}
private int ShiftAndWrap(int value, int positions)
{
positions = positions & 0x1F;
// Save the existing bit pattern, but interpret it as an unsigned integer.
uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
// Preserve the bits to be discarded.
uint wrapped = number >> (32 - positions);
// Shift and wrap the discarded bits.
return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
}
}
public class Example
{
public static void Main()
{
Point pt = new Point(5, 8);
Console.WriteLine(pt.GetHashCode());
pt = new Point(8, 5);
Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 28
// 37
注意:
哈希码用于在基于哈希表的集合中进行有效的插入和查找。哈希码不是永久值。为此原因:
1) 不要序列化哈希码值或将其存储在数据库中。
2) 不要将哈希码用作从键控集合中检索对象的键。
3) 不要跨应用程序域或进程发送哈希码。在某些情况下,可以在每个进程或每个应用程序域的基础上计算哈希码。
4) 如果您需要加密强度高的哈希,请不要使用哈希码代替加密哈希函数返回的值。对于加密哈希,请使用派生自System.Security.Cryptography.HashAlgorithm或System.Security.Cryptography.KeyedHashAlgorithm类的类。
5) 不要测试哈希码是否相等,以确定两个对象是否相等。(不相等的对象可以具有相同的哈希码。)要测试是否相等,请调用ReferenceEquals或Equals方法。
6) 如果重写GetHashCode方法,则还应该重写Equals,反之亦然。如果想要测试重写的Equals方法时,则重写的GetHashCode方法必须为两个对象返回相同的值。
相关文档: