Delphi 如何使用自定义相等比较器创建字典?

Delphi 如何使用自定义相等比较器创建字典?,delphi,Delphi,要创建带有自定义等式的TObjectDictionary,我必须执行以下操作: TObjectDictionary<ansiString, boolean>.create( TDelegatedEqualityComparer<ansiString>.Create( function(const Left, Right: ansiString): Boolean begin Result := ALSameText(Left, Right);

要创建带有自定义等式的
TObjectDictionary
,我必须执行以下操作:

TObjectDictionary<ansiString, boolean>.create(
  TDelegatedEqualityComparer<ansiString>.Create(
   function(const Left, Right: ansiString): Boolean
   begin
     Result := ALSameText(Left, Right);
   end,
   function(const Value: ansiString): Integer
   begin
     // !! here i want the default GetHashCode !! I don't want to write it myself 
   end))
TObjectDictionary.create(
TDelegatedEqualityComparer.Create(
函数(常量左、右:ansiString):布尔值
开始
结果:=ALSameText(左、右);
完,,
函数(常量值:ansiString):整数
开始
//!!这里我想要默认的GetHashCode!!我不想自己写
完)
因此,我需要为函数EqualsGetHashCode给出一个实现。但是我只想为Equals函数给出一个实现。可能吗

但是我只想给Equals函数一个实现。可能吗


不,您必须提供一个哈希函数。这是一本字典所需要的

t字典
TObjectDictionary
需要对键进行散列。如果您想使用
TDelegatedEqualityComparer
,那么您必须为它提供一个生成哈希的函数,这就是它的工作方式

但是,如果您不想从头开始编写自己的哈希代码,您可以利用RTL的本机哈希进行
AnsiString
,例如:

函数(常量值:AnsiString):整数
开始
结果:=TEqualityComparer函数,可从
System.Generics.Defaults
单元公开访问,因此您可以直接调用该函数,例如:

函数(常量值:AnsiString):整数
开始
结果:=BobJenkinsHash(PAnsiChar(Value)^,长度(Value)*SizeOf(AnsiChar),0;
结束
或者,正如函数在XE8+中所述:

警告:
不推荐使用BobJenkinsHash
。请使用

使用
系统哈希;
函数(常量值:AnsiString):整数
开始
结果:=THashBobJenkins.GetHashValue(PAnsiChar(Value)^,Length(Value)*SizeOf(AnsiChar));
结束
但是我只想给Equals函数一个实现。可能吗

不需要。您需要为
EqualityComparison
Hasher
函数提供实现

为了正确地做到这一点,您需要了解字典实现中这些函数的用途

平等比较 Dictionary是存储键值对的集合,其中键必须是唯一的。键的唯一性最终取决于
EqualityComparison
函数。根据该函数的实现,字典将存储和更新与特定键相关的值

例如,带有字符串键的字典可能要求不同的键必须完全匹配,包括大小写。对于这种实现,“abc”和“abc”将是两个不同的键,您可以存储与每个键关联的不同值。这是带有字符串键的Delphi字典的默认实现

abc -> true
ABC -> false
存储上述键值对将产生具有两对的字典。设置“ABC”键值后,仍然可以检索将为真的未更改的“ABC”值

然而,带有字符串键的字典也可以以不区分大小写的方式实现,其中“abc”和“abc”是相同的键。这是您在示例中使用的一种实现

abc -> true
ABC -> false 
将上述键值对存储在不区分大小写的字典中会导致字典只包含一个键值对。存储ABC键值后,原始ABC值将丢失,读取存储在ABC或ABC键值中的值将产生false

理论上,您可以在不需要
Hasher
函数的情况下使用字典实现

哈舍 如果不需要
Hasher
函数,它的用途是什么

哈希函数使从字典中快速检索值成为可能。它根据密钥散列值在存储桶中划分存储的密钥-值对。因此,搜索密钥只在特定的bucket中进行,而不是遍历所有密钥直到找到特定的密钥,在该bucket中,将使用相等性比较来最终确定两个密钥是否匹配

因此,
Hasher
函数需要在程序运行期间为每个唯一键生成相同的哈希值。不同的键可以具有相同的哈希值-可以接受冲突。字典的性能最终取决于哈希函数的性能和冲突数(但是,选择不同主题的最佳哈希函数)

若您需要不区分大小写的字符串字典,那个么默认的哈希函数将不起作用,因为键中的大小写不同会导致不同的哈希值

procedure Test;
var
  d: TDictionary<string, boolean>;
  b: Boolean;
begin
  d := TDictionary<string, boolean>.Create(
  TDelegatedEqualityComparer<string>.Create(
   function(const Left, Right: string): Boolean
   begin
     Result := SameText(Left, Right);
   end,
   function(const Value: string): Integer
   begin
     Result := THashBobJenkins.GetHashValue(PChar(Value)^, Length(Value) * SizeOf(Char));
   end));

  d.AddOrSetValue('abc', true);
  d.AddOrSetValue('ABC', false);

  b := d.Items['abc'];
  Writeln(b); // TRUE 

  b := d.Items['ABC'];
  Writeln(b); // FALSE
end;
这并不是我们想要的。我们希望ABC键的设置值覆盖存储在ABC键中的值

那么如何解决这个问题呢?什么是正确的哈希函数

因为散列函数必须满足的唯一条件是相等的键必须具有相同的散列值,所以最简单(最愚蠢)的实现就是为所有键返回相同的固定整数值——所有键都属于同一个bucket

用下面的函数替换上一个示例中的哈希函数将产生正确的结果

   function(const Value: string): Integer
   begin
     Result := 0;
   end));
然而,这种哑哈希函数将对字典性能产生负面影响。对于大小写不敏感的键,使用相同哈希值的哈希函数稍微好一点,它返回的是字符串长度,而不是固定值

   function(const Value: string): Integer
   begin
     Result := Length(Value);
   end));

这只是一个可能的哈希函数,可以处理不区分大小写的需求。找到更好的哈希函数最终取决于什么是典型的键值——例如,如果字典中的所有键都具有相同的长度,那么基于长度的哈希函数的性能将与固定值函数一样差。

如果要求字符串键不区分大小写,对字符串使用默认哈希函数将无法正常工作,因为
   function(const Value: string): Integer
   begin
     Result := Length(Value);
   end));