使用.Equals()而不是哈希的C#字典
本文关键字:哈希 字典 Equals 使用 | 更新日期: 2023-09-27 18:21:00
是否有像Dictionary这样的数据结构,允许基于为给定类而非哈希值定义的.Equals()调用添加唯一元素。
在我的例子中,我有一个PointD类定义了一个具有十进制X和Y的点。由于十进制类型的性质有点不精确,因此不可能在该点上创建哈希,因为本质上相同的两个点之间的小错误将导致哈希值的大差异。
基本上,我希望能够计算每个x,y组合的点数。有没有现有的机制,或者我需要自己实施?
小心。听起来你想定义Equals,这样在一定容差内的值就被认为是相等的。如果这样做,Equals将不具有传递性,但它需要具有传递性才能使字典发挥作用。
示例:假设x比y小0.8倍公差。他们将被认为是平等的。现在考虑z值,它比y大0.8倍公差。因此,y和z也是相等的。但是x和z是而不是相等!
GetHashCode必须为两个相等的对象返回相同的值。由于等式在这个系统中是不可传递的,您可以证明GetHashCode需要为所有对象返回相同的值,这会导致您的字典像链表一样(但会浪费更多的存储开销)。
您可以通过将所有点四舍五入到一定的精度来解决此问题,并根据四舍五进的值计算哈希代码和等式。当然,这种方法可能有其自身的陷阱。
是的,你可以创建自己的IEqualityComparer,并在构建它时将其传递给字典……它不使用Equal,但你可以让它做你自己的散列。
如果您想在实际的Point类上保留Hash,这将更好地工作。
只需覆盖PointD类上的GetHashCode方法即可满足您的需求,不是吗?
你不可能有一本不使用哈希的字典。字典需要一个散列函数和一个相等比较器。哈希代码用于获取哈希表中的bucket,相等比较器用于检查bucket中的值。最重要的要求是,比较相等的值也必须具有相同的哈希代码。
在你的情况下,我会做的是将点数标准化,只使用一定数量的数字。可以使用Math.Round
方法执行此操作。通过这种方式,可以保留has/equality组合的所有必要属性。
您可以在构造函数中或在Equals
和GetHashCode
方法的重写中进行舍入(您仍然需要)。在构造函数中执行此操作的好处是,您只执行一次计算,同时仍然在各处强制执行需求。如果您的类是可变的,那么您还必须在属性setter和任何直接修改字段的地方执行此操作。
如果你有很多点,最好的办法可能是使用类似Dictionary<Point, List<PointD>>
的东西,将每个PointD
变成Point
。如果X值使得公差范围内的值可以四舍五入到不同的值,则在Dictionary
中存储向上取整和向下取整版本。在查找点时,如果Y值使得公差范围内的值可以四舍五入到不同的值,请在表中查找这两个值。
请注意,Dictionary操作可能返回List<PointD>
的一个或两个实例(只有在Y舍入不明确的情况下才有两个实例;这些列表中的部分或全部PointD
实例可能与实际兴趣点不匹配,但需要检查的实例数量应该是Dictionary
中总数的一小部分。