我可以为以下比较器逻辑编写哈希码函数吗?
如果来自(A,B,C)的至少两个属性匹配,则My
的两个实例相等。
Equals部分很简单,但是我对哈希码部分感到困惑,我的一部分认为它可能不可能。
class MyOtherComparer : IEqualityComparer<My>
{
public bool Equals(My x, My y)
{
if (Object.ReferenceEquals(x, y))
return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
int matches = 0;
if(x.A == y.A) matches++;
if(x.B == y.B) matches++;
if(x.C == y.C) matches++;
// match on two out of three
return (matches > 1)
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(My x)
{
// ???
}
}
更新:除了Reed Copsey的正确答案之外,Ethan Brown清楚地说明了关于模糊比较器一般有用性的一个非常重要的观点 - 请查看他的答案以及完整的答案了解这个问题/答案的基础。
答案 0 :(得分:5)
是的,这是可能的。最简单的实现是始终返回常量。
public int GetHashCode(My x)
{
return 0;
}
GetHashCode文档声明:
需要实现以确保如果Equals方法对两个对象x和y返回true,则x的GetHashCode方法返回的值必须等于为y返回的值。
但是,您可以完全自由地为两个不相等的对象返回相同的哈希码。
话虽这么说,这可能会导致某些算法执行得非常糟糕,因为你会遇到很多哈希冲突。但是,鉴于您的奇/唯一等式检查的性质,可能需要这样做。
请注意,这在任何情况下都会有问题。根据您的逻辑,可以有三个对象,comparer.Equals(foo, bar)==true
和comparer.Equals(foo, baz)==true
但comparer.Equals(baz, bar)==false
。在使用IEqualityComparer<T>
的许多情况下,这可能会出现问题。
答案 1 :(得分:1)
对于两个相同的对象,哈希码必须相同,但对于两个不同的对象,哈希码不必相同。您可以为所有对象返回相同的值以满足IEqualityComparer
个使用者的需求,但我不知道在您的情况下从哈希获得任何速度优势。
答案 2 :(得分:1)
我可以为以下比较器逻辑编写哈希码函数吗?
是。您始终可以为任何内容编写哈希代码。问题是它的效率如何。无论如何,你总能拥有:
public int GetHashCode()
{
return 0;
}
它总是工作,但它的效率非常低*。
答案 3 :(得分:1)
假设我们有2个对象A,B。它们中的每一个都具有属性p1,p2和p3。假设A.p1 == B.p1和A.p3 == B.p3,如果散列函数取决于p2,它对于A和B将是不同的,因此它们不相等。如果你想根据p1和p3计算散列函数,有很多例子,散列函数不会返回正确的散列值,许多相等的对象将不相等。我想我们不能有一个变量函数。您可以使用常量,但如果要将其用作字典或散列表中的散列键,则不会获得接近O(1)的复杂度。
答案 4 :(得分:1)
到达非常量哈希函数的核心问题是,您无法确保跨平等的传递性。通常,平等被认为是可传递的。也就是说,A = B和B = C意味着A = C(这进一步意味着A,B和C都将具有相同的哈希码)。但是,根据您对等式的定义,您可以得到A = B,B = C和A!= C.理想情况下,不相等的元素将具有不同的哈希码,因此A和C将具有不同的哈希码;但它们不能,因为它们都等于B,所以它们都必须具有相同的哈希码。
如果您对整个集合有所了解,那么您可以获得非常量哈希函数的唯一方法就是这样。您必须将集合分区为“相等的bin”,其中bin中的每个元素都等于bin中的其他元素(包括bin的可能性)。完成此分区后,您可以使用它来生成非常量算法(假设您有多个bin)来生成哈希码。
关于平等箱的想法是,可能有许多这样的箱子配置。作为选择标准,您可能希望最大化bin的数量(以改善散列表查找性能)。退化案例(如Reed Copsey的正确答案所示)是你把所有东西放在同一个箱子里(但是,正如超级猫在下面的评论中指出的那样,名称“平等箱”然后变得具有误导性)。这并没有违反哈希值的任何约束,但是它会导致算法中的性能不佳,这些算法期望具有产生非退化分区的值。
正如supercat在下面指出的那样,为了满足哈希值的约束,必须满足以下条件:如果两个元素位于两个不同的bin中,则它们必须不相等(但是,同一个bin中的两个元素不必相等)是平等的。
答案 5 :(得分:0)
看到您的真正问题是处理Except扩展方法, 我决定为你推荐一些东西,虽然不是真的答案。
public class EqualityComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _comparer;
private readonly Func<T, int> _hashCoder;
public EqualityComparer(Func<T, T, bool> comparer, Func<T, int> hashCoder = null)
{
if (comparer == null)
{
throw new ArgumentNullException("comparer");
}
this._comparer = comparer;
this._hashCoder = hashCoder ?? (x => 0);
}
public bool Equals(T x, T y)
{
return this._comparer(x, y);
}
public int GetHashCode(T obj)
{
return this._hashCoder(obj);
}
}
然后你可以这样使用它:
arr1.Except(arr2, new EqualityComparer<dynamic>((x, y) =>
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) ||
ReferenceEquals(y, null))
return false;
var matches = 0;
if (x.A == y.A) matches++;
if (x.B == y.B) matches++;
if (x.C == y.C) matches++;
return (matches > 1);
}));