如何在Delphi TStringList中更快地搜索名称/值对?

如何在Delphi TStringList中更快地搜索名称/值对?,delphi,tstringlist,Delphi,Tstringlist,我在一个应用程序中实现了语言转换,方法是在运行时将所有字符串放在一个TStringList中,其中包含: procedure PopulateStringList; begin EnglishStringList.Append('CAN_T_FIND_FILE=It is not possible to find the file'); EnglishStringList.Append('DUMMY=Just a dummy record'); // total of 2000

我在一个应用程序中实现了语言转换,方法是在运行时将所有字符串放在一个TStringList中,其中包含:

procedure PopulateStringList;
begin  
  EnglishStringList.Append('CAN_T_FIND_FILE=It is not possible to find the file');
  EnglishStringList.Append('DUMMY=Just a dummy record');
  // total of 2000 record appended in the same way
  EnglishStringList.Sorted := True; // Updated comment: this is USELESS!
end;
然后我使用以下方法获得翻译:

function GetTranslation(ResStr:String):String;
var
  iIndex : Integer;
begin
  iIndex := -1;
  iIndex :=  EnglishStringList.IndexOfName(ResStr);
  if iIndex >= 0 then
  Result :=  EnglishStringList.ValueFromIndex[iIndex] else
  Result := ResStr + ' (Translation N/A)';
end;
无论如何,这种方法大约需要30微秒来定位一条记录,有没有更好的方法来达到同样的结果

更新:为了将来的参考,我在这里写了一个新的实现,它按照建议使用TDictionary(适用于Delphi 2009及更新版本)

procedure PopulateStringList;
begin  
  EnglishDictionary := TDictionary<String, String>.Create;
  EnglishDictionary.Add('CAN_T_FIND_FILE','It is not possible to find the file');
  EnglishDictionary.Add('DUMMY','Just a dummy record');
  // total of 2000 record appended in the same way
end;


function GetTranslation(ResStr:String):String;
var
  ValueFound: Boolean;
begin
  ValueFound:=  EnglishDictionary.TryGetValue(ResStr, Result);
  if not ValueFound then Result := Result + '(Trans N/A)';
end;
procedure PopulateStringList;
开始
EnglishDictionary:=t字典。创建;
EnglishDictionary.Add('CAN___查找文件','It's not canble to FIND the FILE');
添加('DUMMY','Just a DUMMY record');
//以相同方式追加的2000条记录总数
结束;
函数GetTranslation(ResStr:String):String;
变量
ValueFound:布尔型;
开始
ValueFound:=EnglishDictionary.TryGetValue(ResStr,结果);
如果未找到ValueFound,则结果:=结果+'(不适用);
结束;

新的GetTranslation函数的执行速度(在我的2000个示例记录上)比第一个版本快1000倍。

我认为应该更好。

如果THashedStringList适合您,那就太好了。它最大的缺点是,每次更改列表的内容时,都会重建哈希表。所以,只要你的列表仍然很小或者不经常改变,它就会对你有用

有关这方面的更多信息,请参阅:,其中提供了一些备选方案


如果您有一个可能会更新的大列表,您可能希望通过尝试,而不必在每次更改时重新计算整个表。

我认为您没有正确使用英语tringlist(TStringList)。这是一个已排序的列表,您可以添加元素(字符串),对其进行排序,但在搜索时,您可以通过部分字符串(只有名称,带有IndexOfName)进行排序

如果在排序列表中使用IndexOfName,则TStringList不能使用二分法搜索。它使用顺序搜索

(这是IndexOfName的实现)

对于结果:=0到GetCount-1 do
开始
S:=获取(结果);
P:=AnsiPos('=',S);
如果(p0)和(CompareStrings)(Copy(S,1,P-1),Name)=0,则退出;
结束;
我认为这是业绩不佳的原因。
另一种选择是使用2 TStringList:
*第一个(已排序)仅包含“名称”和指向包含值的第二个列表的指针;您可以使用对象属性的“指针”实现指向第二个列表的指针。
*第二个(未排序)列表包含这些值

当你搜索时,你会在第一个列表中进行搜索;在这种情况下,您可以使用查找方法。当您找到名称时,指针(使用Object属性实现)将为您提供第二个列表中的位置和值

在这种情况下,排序列表上的Find方法比HashList(它必须执行一个函数来获取值的位置)更有效

问候


请原谅我的英语错误

在Delphi 2009或更高版本中,我会在Generics.Collections中使用TDictionary
还请注意,还有一些免费工具,例如用于翻译应用程序的工具。

您还可以使用类帮助程序重新编程“IndexOfName”函数:

TYPE
  TStringsHelper = CLASS HELPER FOR TStrings
                     FUNCTION IndexOfName(CONST Name : STRING) : INTEGER;
                   END;

FUNCTION TStringsHelper.IndexOfName(CONST Name : STRING) : INTEGER;
  VAR
    SL  : TStringList ABSOLUTE Self;
    S,T : STRING;
    I   : INTEGER;

  BEGIN
    IF (Self IS TStringList) AND SL.Sorted THEN BEGIN
      S:=Name+NameValueSeparator;
      IF SL.Find(S,I) THEN
        Result:=I
      ELSE IF (I<0) OR (I>=Count) THEN
        Result:=-1
      ELSE BEGIN
        T:=SL[I];
        IF CompareStrings(COPY(T,1,LENGTH(S)),S)=0 THEN Result:=I ELSE Result:=-1
      END;
      EXIT
    END;
    Result:=INHERITED IndexOfName(Name)
  END;
类型
TStringsHelper=Tstring的类帮助器
函数IndexOfName(CONST Name:STRING):整数;
结束;
函数TStringsHelper.IndexOfName(CONST Name:STRING):整数;
变量
SL:绝对自我;
S、 T:弦;
I:整数;
开始
如果(Self是TStringList)和SL.已排序,则开始
S:=名称+名称值分隔符;
如果SL.Find(S,I)那么
结果:=I
否则,如果(I=计数),则
结果:=-1
否则开始
T:=SL[I];
如果CompareStrings(COPY(T,1,LENGTH(S)),S=0,则结果:=I,否则结果:=-1
结束;
出口
结束;
结果:=继承的IndexOfName(名称)
结束;
(如果您不喜欢类帮助程序或在Delphi版本中没有它们,也可以在子代TStrings类中实现它)


这将对已排序的TStringList使用二进制搜索,对其他TStrings类使用顺序搜索。

谢谢!!!!现在识别45条记录,从50毫秒到2毫秒,速度快25倍!酷-不知怎么的,我从来不知道这件事。我将不得不研究它。我不需要更新列表,它只填充了一次,因此我认为我可以继续使用THashedStringList,因为现在瓶颈已经消失。谢谢,是的,我知道tdxggettext,但我描述的翻译的实现已经由以前的开发人员完成,所以我正在尝试改进它,使用THasedStringList,我获得了令人满意的结果。无论如何,我会尝试一下(以我个人的知识来看,您给出的TDictionary建议)。@VilleK:TDictionary和Delphi 2009中的一些其他泛型中存在一些bug,因此最好避免使用它,除非您使用Delphi 2010或XE。老实说,关于如何使用它及其功能的文档很少。是的,我记得在Delphi 2009中TDictionary有一个bug,但现在我在Delphi 2010中经常使用它,并且没有遇到任何问题。我觉得效果很好。此外,同一单元中的TObjectDictionary非常有用,因为它可以选择同时拥有键和值。对我来说,Generics.Collections单元是从较旧的Delphi版本升级的主要原因之一。我有Delpgi 2009,我第一次尝试使用建议的TDictionary。。。就我而言,它比THashedStringList快40倍。我只使用TDictionary方法Add和TryGetValue。2009年我是否“安全”?所以我接受这个答案,因为现在我的速度快了1000倍。TStringList给THashedStringList的是x25,THashedStringList给TDictionary的是额外的x40。总计x1000。谢谢@us
TYPE
  TStringsHelper = CLASS HELPER FOR TStrings
                     FUNCTION IndexOfName(CONST Name : STRING) : INTEGER;
                   END;

FUNCTION TStringsHelper.IndexOfName(CONST Name : STRING) : INTEGER;
  VAR
    SL  : TStringList ABSOLUTE Self;
    S,T : STRING;
    I   : INTEGER;

  BEGIN
    IF (Self IS TStringList) AND SL.Sorted THEN BEGIN
      S:=Name+NameValueSeparator;
      IF SL.Find(S,I) THEN
        Result:=I
      ELSE IF (I<0) OR (I>=Count) THEN
        Result:=-1
      ELSE BEGIN
        T:=SL[I];
        IF CompareStrings(COPY(T,1,LENGTH(S)),S)=0 THEN Result:=I ELSE Result:=-1
      END;
      EXIT
    END;
    Result:=INHERITED IndexOfName(Name)
  END;