2

如果我使用 TDictionary 在 Delphi 中实现关系 Car <-> Owner,我应该如何实现 IEqualityComparer 的 Equals 和 GetHashCode 函数?(GetHashCode 返回一个 Integer,用于 TDictionary 中的散列。)

对于 TVehicle 类,假设它有一个 VIN(车辆识别号)。

我应该如何实现 VIN 的哈希码?

更新:在这个例子中,对象身份并不意味着“两个对象指针的内存位置的身份”,而是“同一对象的两个实例的身份,基于其属性的唯一和不变(“不可变”)组合” .

因此,我不需要通过地图中的内存地址搜索车辆,而是需要具有我正在寻找的 id 的车辆。

考虑一个包含车主数据的数据库,在应用程序启动时加载到字典中。现在,如果用户在申请表中输入 VIN,应用程序如何在字典中找到车辆?VehicleFactory.CreateVehicleFromDatabase(Edit1.Text);如果代码使用并在字典中搜索此对象创建一个新实例,则 Equals 的默认实现将不会在映射中找到任何条目,因为它会查找内存地址。为了找到车辆,Equals 需要比较 VIN。

所以我必须创建一个自定义的 IEqualityComparer。实现 Equals 是微不足道的。但是 GetHashCode 呢?对于字符串属性,我不能简单地使用字符串的地址(请参阅Are Delphi strings immutable中的 Berry Kelly :“如果您从两个单独的代码部分创建相同的字符串,它们将不会共享相同的后备存储”) ,因此字符串属性的 GetHashCode 函数需要自定义实现。

我还发现我发现了如何使用 Delphi 散列字符串?- 有一个例子,其中包含HashValue('Hello World')

4

3 回答 3

3

如果可能的话,我会把 KISS 原则放在这个问题上。如果您的实际钥匙是 ID 而不是车辆本身,那么为什么不使用 aTDictionary<string, TPerson>而不是TDictionary<TVehicle, TPerson>?然后你就不必担心自定义比较器了。

于 2011-03-15T18:30:20.457 回答
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 或其他界面中的任何内容保持一致.

于 2011-03-15T20:51:34.197 回答
3

您似乎被误导相信 Delphi 字符串不附带默认的哈希码实现。

不是这种情况。当您TDictionary使用字符串值作为键创建 a 时,会根据字符串的内容计算哈希值。如果Value是字符串变量,则代码如下所示:

BobJenkinsHash(Value[1], Length(Value) * SizeOf(Value[1]), 0);

我认为这回答了您关于字符串哈希的部分问题。


对其他答案的评论以及我已删除的评论是对您正在考虑的设计问题的有趣讨论。我仍然怀疑您认为正确的解决方案是允许 TVehicle 实例和 VIN 之间的多对一关系。

您已确认您不能有多个具有相同 VIN 但数据不同的 TVehicle 实例。在我看来,实现这一目标的最佳方法是确保 TVehicle 实例和 VIN 之间存在一对一的关系。

这种一对一的关系很容易实现。您需要使 TVehicle 实例的实例化成为工厂类的私有函数。该工厂类包含一个包含现有车辆实例的字典TDictionary<string,TVehicle>。如果您需要获取车辆,请向工厂索取。它要么返回位于其字典中的现有字典,要么合成一个新字典。

毫无疑问,还有许多其他方法可以实现这种效果,但我强烈建议您考虑一种方法,即每个 VIN 只产生一个车辆实例。

于 2011-03-15T21:09:15.980 回答