我在C#中有以下结构来表示图形边缘:
struct Edge
{
public Edge(int leftA, int leftB, int leftC, int leftD, int rightA, int rightB, int rightC, int rightD)
{
LeftIdA = leftA;
LeftIdB = leftB;
LeftIdC = leftC;
LeftIdD = leftD;
RightIdA = rightA;
RightIdB = rightB;
RightIdC = rightC;
RightIdD = rightD;
}
public readonly int LeftIdA;
public readonly int LeftIdB;
public readonly int LeftIdC;
public readonly int LeftIdD;
public readonly int RightIdA;
public readonly int RightIdB;
public readonly int RightIdC;
public readonly int RightIdD;
}
并且需要在HashSet中存储大量(大约5百万),因此没有重复项。什么是GetHashCode的良好实现,因此它针对速度进行了优化?
我试图将每个id的4位存储在返回的整数中,如下所示:
public override int GetHashCode()
{
int A = LeftIdA & 0xF;
int B = LeftIdB & 0xF;
int C = LeftIdC & 0xF;
int D = LeftIdD & 0xF;
int E = RightIdA & 0xF;
int F = RightIdB & 0xF;
int G = RightIdC & 0xF;
int H = RightIdD & 0xF;
int result = A;
result = (result << 4) | B;
result = (result << 4) | C;
result = (result << 4) | D;
result = (result << 4) | E;
result = (result << 4) | F;
result = (result << 4) | G;
result = (result << 4) | H;
return result;
}
但它比将项目添加到列表中要慢80%。
答案 0 :(得分:1)
什么是GetHashCode的良好实现,因此它针对速度进行了优化?
由于您的所有字段都是只读的,因此最好的办法是在构造函数中预先计算哈希码,然后从GetHashCode
返回。
要预先计算哈希码,您可以使用Guffa答案中的公式。
答案 1 :(得分:0)
添加到HashSet
需要更长时间,而且不是因为任何糟糕的策略
GetHashCode()
实施。实际上,这种实现看起来相当不错。一个HashSet
必须做各种疯狂的废话,比如设置水桶并将东西插入其中。
性能增益在于发现hashset中的元素。尝试将500万个不同的项添加到列表和哈希集,并查看哪个容器能够更快地告诉您它是否包含特定Edge。您可能愿意支付不到两倍的设置时间。
答案 2 :(得分:0)
为了达到最佳效果,哈希码应该尽可能少地进行冲突,即产生尽可能多的哈希码。
尝试生成哈希码,以便使用所有成员的所有数据:
public override int GetHashCode() {
return
LeftIdA ^ LeftIdB ^ LeftIdC ^ LeftIdD ^
RightIdA ^ RightIdB ^ RightIdC ^ RightIdD;
}
与素数相乘可以得到非常好的分布,因此您应该测试是否能在您的情况下提供更好的性能:
public override int GetHashCode() {
return
((((((LeftIdA * 251 + LeftIdB) * 251 + LeftIdC) * 251 +
LeftIdD) * 251 + RightIdA) * 251 + RightIdB) * 251 +
RightIdC) * 251 + RightIdD;
}
注意:确保您还为结构提供了优化的相等性比较。默认实现将使用反射来确定要比较的所有成员,因此非常慢。
我做了一些测试,在第二个实现中,我可以在大约两秒钟内向HashSet添加500万个项目。