如何为TDictionary创建不区分大小写的TEqualityComparer?

13

我有一个使用 TDictionary 的情况:

var D: TDictionary<string, integer>;
begin
  D := TDictionary<string, integer>.Create(TCustomEqualityComparer.Create());
  try
    D.Add('One', 1);
    D.Add('Two', 2);
    D.Add('Three', 3);

    showmessage(inttostr(D.Items['One']));
    showmessage(inttostr(D.Items['TWO']));
  finally
    D.Free;
  end;
end;

类TCustomEqualityComparer是从Generics Defaults TEqualityComparer(Delphi)复制而来,并对GetHashCode方法进行了微小修改:

TCustomEqualityComparer = class(TEqualityComparer<string>)
public
  function Equals(const Left, Right: string): Boolean; override;
  function GetHashCode(const Value: string): Integer; override;
end;

function TCustomEqualityComparer.Equals(const Left, Right: string): Boolean;
begin
  Result := SameText(Left, Right);
end;

function TCustomEqualityComparer.GetHashCode(const Value: string): Integer;
begin
  Result := BobJenkinsHash(Value[1], Length(Value) * SizeOf(Value[1]), 0);
end;

我希望 TCustomEqualityComparer 能够对键值执行不区分大小写的匹配。例如:

D.Items['TWO']

然而,我得到了一个“未找到项目”的异常。我正在使用Delphi 2010版本14.0.3513.24210。

有人知道我的代码哪里出了问题吗?

3个回答

20
uses System.Generics.Collections, System.Generics.Defaults;

var
  D: TDictionary<string, Integer>;
begin
  D := TDictionary<string, Integer>.Create(TIStringComparer.Ordinal); // ‹- this is the trick
  try
    D.Add('One', 1);
    .
    .
  finally
    D.Free;
  end;
end;

1
我知道这是一个旧答案,但我认为它提供了与问题相反的答案。根据System.Generics.Defaults.TIStringComparer.Ordinal的帮助文件,它“返回执行区分大小写字符串比较的TStringComparer对象”,而不是执行不区分大小写字符串比较的对象。 - Adam G
@AdamG - 这一定是文档中的错误,我刚刚测试了一下,它进行了不区分大小写的比较。这也可以解释InsensitiveStringComparer类名称中的I。 - martinstoeckli

3

当所有返回 Equals = true 的值的 HashCode 相同时,可以尝试在将其发送到 HashFunction 之前将 Value 转换为大写字母。


你确定原始的Equals方法已经忽略大小写了吗?这是在Generics.Defaults.pas单元中定义的原始Equals方法:function Equals_UString(Inst: PSimpleInstance; const Left, Right: UnicodeString): Boolean; begin Result := Left = Right; end; - Chau Chee Yang
你是对的!我混淆了原始实现和一个例子。 - Uwe Raabe

2

谢谢。我已经修改了TCustomEqualityComparer.GetHashCode,按照您的要求运行良好:

function TCustomEqualityComparer.Equals(const Left, Right: string): Boolean;
begin
  {$IFDEF UNICODE }
    Result := (Left.ToUpper = Right.ToUpper);
  {$ELSE }
    Result := SameText(Left, Right);
  {$ENDIF }
end;

function TCustomEqualityComparer.GetHashCode(const Value: string): Integer;
var s: string;
begin
  s := {$IFDEF UNICODE } Value.ToUpper {$ELSE } UpperCase(Value) {$ENDIF };
  Result := BobJenkinsHash(s[1], Length(s) * SizeOf(s[1]), 0);
end;

UpperCase / SameText 仅适用于 ASCII 文本(仅处理 'a'..'z')。在 Delphi 的 UNICODE 版本中使用 .ToUpper。代码片段已更新... - HeartWare

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接