如果我实施关系Car< - >使用TDictionary的Delphi中的所有者,我应该如何实现IEqualityComparer的Equals和GetHashCode函数? (GetHashCode返回一个Integer,用于在TDictionary中进行散列。)
对于TVehicle课程,假设它有一个VIN(车辆识别号码)。
我应该如何为VIN实现哈希码?
更新:在此示例中,对象标识不表示“两个对象指针的内存位置的标识”,而是“同一对象的两个实例的标识,基于唯一且不变(” immutable < / em>“)其属性的组合'。
因此,我不需要通过地图中的记忆地址搜索车辆,而是需要具有我正在寻找的ID的车辆。
想想一个包含车辆所有者数据的数据库,在应用程序启动时加载到字典中。现在,如果用户在申请表中输入VIN,应用程序如何在字典中找到车辆?如果代码使用VehicleFactory.CreateVehicleFromDatabase(Edit1.Text);
创建新实例并在字典中搜索此对象,则Equals的默认实现将不会在映射中找到任何条目,因为它会查找内存地址。要找到车辆,Equals需要比较VIN。
所以我必须创建一个自定义的IEqualityComparer。实施Equals是微不足道的。但GetHashCode怎么样?对于字符串属性,我不能简单地使用字符串的地址(请参阅Are Delphi strings immutable?中的Berry Kelly:“如果从两个单独的代码段创建相同的字符串,它们将不共享相同的后备存储”) ,因此字符串属性的GetHashCode函数需要自定义实现。
我还发现问题How do I hash a string with Delphi? - 有一个示例包含HashValue('Hello World')
答案 0 :(得分:3)
TDictionary<string, TPerson>
代替TDictionary<TVehicle, TPerson>
?那么您就不必担心自定义比较器了。
答案 1 :(得分:3)
当你被告知设计中的气味和其他东西时,我会回答你的问题,因为创建一个对象键控字典并根据与键的内存地址不同的任何内容进行比较是有效的:
您可以在TDictionary创建时创建一个新的比较器。
例如:
type
TVehicleOwner = class (TDictionary<TVehicle, TOwner>)
end;
//other code here
procedure TForm2.Button1Click(Sender: TObject);
var
VehOwner: TVehOwner;
begin
VehOwner := TVehOwner.Create(TEqualityComparer<TVehicle>.Construct(
//comparer
function(const Left, Right: TVehicle): Boolean
begin
{ Make a case insensitive comparison }
Result := CompareText(Left.FID, Right.FID) = 0;
end,
//hasher
function(const Value: TVehicle): Integer
begin
{ Generate a hash code. }
Result := TheHashAlgorythmOfYourChoice(Value.FID);
end)
);
//more code here
这就是说,如果您有两个代表同一对象的实例,我认为这是您代码中的一个缺陷。如果你的内存中有一个ID为“ABC”的TVehicle,对我来说,这应该是该车辆的唯一实例,你必须提供一些方法来为你的所有代码获得相同的实例。这样,您可以在不编写自定义比较器的情况下使用Dictionary类,但更重要的是,您知道您一直在使用相同的对象,并且您的应用程序状态将与代码,UI或其他接口中的任何内容保持一致
答案 2 :(得分:3)
您似乎误以为Delphi字符串没有默认的哈希码实现。
事实并非如此。当您使用字符串值作为键创建TDictionary
时,将根据字符串的内容计算哈希值。如果Value
是一个字符串变量,那么代码如下所示:
BobJenkinsHash(Value[1], Length(Value) * SizeOf(Value[1]), 0);
我认为这回答了关于字符串哈希问题的部分内容。
对其他答案的评论以及我删除的评论是对您正在考虑的设计问题的有趣讨论。我仍然怀疑你认为正确的解决方案是允许TVehicle实例和VIN之间的多对一关系。
您已确认您不得拥有多个具有相同VIN但数据不同的TVehicle实例。在我看来,实现这一目标的最佳方法是确保您在TVehicle实例和VIN之间建立一对一的关系。
这种一对一的关系很容易实现。您需要将TVehicle实例的实例化设置为工厂类的私有函数。该工厂类包含一个包含现有车辆实例的字典TDictionary<string,TVehicle>
。如果您需要抓住车辆,请向工厂询问。它返回位于其字典中的现有字符,或者合成一个新字典。
毫无疑问,还有其他一些方法可以达到这种效果,但我强烈建议您考虑一种方法,即每个VIN只能产生一个车辆实例。