此示例再次是我实际问题的一个非常简化的版本,涉及linq分组的自定义比较器。我做错了什么?
以下代码生成以下结果 (1.2,0), (4.1,0),(4.1,0), (1.1,0),
然而,由于1.1和1.2是<&lt; 1&lt; 1.0分开。 (1.2,0),(1.1,0), (4.1,0),(4.1,0),class Program
{
static void Main(string[] args)
{
IEnumerable<Point> points = new List<Point> {
new Point(1.1, 0.0)
, new Point(4.1, 0.0)
, new Point(1.2, 0.0)
, new Point(4.1, 0.0)
};
foreach (var group in points.GroupBy(p => p, new PointComparer()))
{
foreach (var num in group)
Console.Write(num.ToString() + ", ");
Console.WriteLine();
}
Console.ReadLine();
}
}
class PointComparer : IEqualityComparer<Point>
{
public bool Equals(Point a, Point b)
{
return Math.Abs(a.X - b.X) < 1.0;
}
public int GetHashCode(Point point)
{
return point.X.GetHashCode()
^ point.Y.GetHashCode();
}
}
class Point
{
public double X;
public double Y;
public Point(double p1, double p2)
{
X = p1;
Y = p2;
}
public override string ToString()
{
return "(" + X + ", " + Y + ")";
}
}
答案 0 :(得分:8)
使用相等比较器的分组算法(我认为所有LINQ方法)总是先比较哈希码,如果两个哈希码相等,则只执行$('.dropdown').click(function() {
$('.show').toggle('.show');
});
。如果在相等比较器中添加跟踪语句,则可以看到:
Equals
结果是:
class PointComparer : IEqualityComparer<Point>
{
public bool Equals(Point a, Point b)
{
Console.WriteLine("Equals: point {0} - point {1}", a, b);
return Math.Abs(a.X - b.X) < 1.0;
}
public int GetHashCode(Point point)
{
Console.WriteLine("HashCode: {0}", point);
return point.X.GetHashCode()
^ point.Y.GetHashCode();
}
}
仅执行具有相同哈希码HashCode: (1.1, 0)
HashCode: (4.1, 0)
HashCode: (1.2, 0)
HashCode: (4.1, 0)
Equals: point (4.1, 0) - point (4.1, 0)
(1.1, 0),
(4.1, 0), (4.1, 0),
(1.2, 0),
的两个点。
现在你可以通过总是返回Equals
作为哈希码来欺骗比较。如果这样做,输出将是:
0
现在已经执行了每对HashCode: (1.1, 0)
HashCode: (4.1, 0)
Equals: point (1.1, 0) - point (4.1, 0)
HashCode: (1.2, 0)
Equals: point (4.1, 0) - point (1.2, 0)
Equals: point (1.1, 0) - point (1.2, 0)
HashCode: (4.1, 0)
Equals: point (4.1, 0) - point (4.1, 0)
(1.1, 0), (1.2, 0),
(4.1, 0), (4.1, 0),
,并且您已经进行了分组。
但是...
什么是“平等”?如果你添加另一个点Equals
,你想在一个组中得到哪些点?使用符号(2.1, 0.0)
进行模糊等式,我们有 -
≈
但
1.1 ≈ 1.2
1.2 ≈ 2.1
这意味着1.1 !≈ 2.1
和1.1
永远不会在一个组中(他们的2.1
永远不会通过)而它取决于点的顺序 Equals
或1.1
是否与2.1
分组。
所以你在这里滑坡。通过邻近聚类点远非微不足道。您正在进入cluster analysis。
的领域答案 1 :(得分:5)
不要忘记GetHashCode
的影响。期望GetHashCode
将始终为任何两个对象返回相同的值,每个Equals
将返回true。如果你没有达到这个期望,你就会得到意想不到的结果。
具体来说,GroupBy
可能使用哈希表之类的东西来允许它将项目组合在一起,而不必将每个项目与其他项目进行比较。如果GetHashCode
返回的值不会将两个对象放入哈希表的同一个桶中,那么它会假设它们不相等,并且永远不会尝试调用Equals
就可以了。
您会发现,当您试图找出GetHashCode
的正确实施时,您尝试对对象进行分组存在根本问题。如果您的x值为1.0
,1.6
和2.2
,那么您会期望什么? 1.0
和2.2
彼此之间的距离太远而无法归入同一群组,但1.6
与其他两个点之间的距离足够接近它应与他们在同一群组中。因此,您的Equals
方法会破坏相等的Transitive属性:
每当A = B且B = C时,则A = C
如果您尝试进行群集分组,则需要使用更加不同的数据结构和算法。如果你只是试图规范点数&#39;在某种程度上,您可能只能说points.GroupBy(p => (int)p.X)
并完全避免使用相等比较器。