【问题标题】:How to create a case in-sensitive TEqualityComparer for TDictionary?如何为 TDictionary 创建不区分大小写的 TEqualityComparer?
【发布时间】:2009-10-23 06:11:09
【问题描述】:

我有一个使用 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。

有人知道我的代码出了什么问题吗?

【问题讨论】:

    标签: delphi


    【解决方案1】:
    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;
    

    【讨论】:

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

    对于所有返回 Equals = true 的值,HashCode 必须相同!尝试在 GetHashCode 中将 Value 设置为大写,然后再将其发送到您的 HashFunction。

    【讨论】:

    • 您确定原来的 Equals 已经不区分大小写了吗?这是在单元 Generics.Defaults.pas 中定义的原始 Equals 方法: function Equals_UString(Inst: PSimpleInstance; const Left, Right: UnicodeString): Boolean;开始结果:=左=右;结束;
    • 你是对的!我把原来的实现和一个例子混在一起了。
    【解决方案3】:

    谢谢。我已经更改了 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')。在 UNICODE 版本的 Delphi 中使用 .ToUpper。代码 sn-p 更新...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-14
    相关资源
    最近更新 更多