简体   繁体   English

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

[英]How can I search faster for name/value pairs in a Delphi TStringList?

I implemented language translation in an application by putting all strings at runtime in a TStringList with: 我通过在运行时将所有字符串放在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;

Then I get the translation using: 然后我使用以下方式获得翻译:

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;

Anyway with this approach it takes about 30 microseconds to locate a record, is there a better way to achieve the same result? 无论如何使用这种方法找到一条记录需要大约30微秒,是否有更好的方法来实现相同的结果?

UPDATE: For future reference I write here the new implementation that uses TDictionary as suggested (works with Delphi 2009 and newer) : 更新:为了将来的参考,我在这里写了建议使用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;

The new GetTranslation function performs 1000 times faster (on my 2000 sample records) then the first version. 新的GetTranslation函数比第一个版本快1000倍(在我的2000个样本记录上)。

我想, THashedStringList应该更好。

In Delphi 2009 or later I would use TDictionary< string,string > in Generics.Collections. 在Delphi 2009或更高版本中,我将在Generics.Collections中使用TDictionary <string,string>。 Also note that there are free tools such as http://dxgettext.po.dk/ for translating applications. 另请注意,有一些免费工具,如http://dxgettext.po.dk/,用于翻译应用程序。

If THashedStringList works for you, that's great. 如果THashedStringList适合你,那很好。 Its biggest weakness is that every time you change the contents of the list, the Hash table is rebuilt. 它最大的缺点是每次更改列表的内容时,都会重建哈希表。 So it will work for you as long as your list remains small or doesn't change very often. 因此,只要您的列表仍然很小或者不经常更改,它就会对您有用。

For more info on this, see: THashedStringList weakness , which gives a few alternatives. 有关这方面的更多信息,请参阅: THashedStringList弱点 ,它提供了一些替代方案。

If you have a big list that may be updated, you might want to try GpStringHash by gabr , that doesn't have to recompute the whole table at every change. 如果您有可能会被更新的大名单,你可能想尝试GpStringHash贾布尔 ,即不必在每次改变重新计算整个表。

I think that you don't use the EnglishStringList(TStringList) correctly. 我认为你没有正确使用EnglishStringList(TStringList)。 This is a sorted list, you add elements (strings), you sort it, but when you search, you do this by a partial string (only the name, with IndexOfName ). 这是一个排序列表,您添加元素(字符串),然后对其进行排序,但在搜索时,您可以通过部分字符串(仅使用IndexOfName的名称)来执行此操作。

If you use IndexOfName in a sorted list, the TStringList can't use Dicotomic search. 如果在排序列表中使用IndexOfName ,则TStringList不能使用Dicotomic搜索。 It use sequential search. 它使用顺序搜索。

(this is the implementation of IndexOfName) (这是IndexOfName的实现)

  for Result := 0 to GetCount - 1 do
  begin
    S := Get(Result);
    P := AnsiPos('=', S);
    if (P <> 0) and (CompareStrings(Copy(S, 1, P - 1), Name) = 0) then Exit;
  end;

I think that this is the reason of poor performance. 我认为这是表现不佳的原因。
The alternative is use 2 TStringList: 另一种方法是使用2 TStringList:
* The first (sorted) only containts the "Name" and a pointer to the second list that contain the value; *第一个(已排序)仅包含“名称”和指向包含该值的第二个列表的指针; You can implement this pointer to the second list using the "pointer" of Object property. 您可以使用Object属性的“指针”将此指针实现到第二个列表。
* The second (not sorted) list containt the values. *第二个(未排序的)列表包含值。

When you search, you do it at first list; 搜索时,您可以在第一个列表中进行搜索; In this case you can use the Find method. 在这种情况下,您可以使用Find方法。 when you find the name, the pointer (implemented with Object property) give you the position on second list with the value. 当您找到名称时,指针(使用Object属性实现)会在第二个列表中为您提供值。

In this case, Find method on Sorted List is more efficient that HashList (that must execute a funcion to get the position of a value). 在这种情况下,Sorted List上的Find方法比HashList(必须执行一个函数来获取值的位置)更有效。

Regards. 问候。

Pd:Excuse-me for mistakes with english. 钯:请原谅我的英语错误。

You can also use a CLASS HELPER to re-program the "IndexOfName" function: 您还可以使用CLASS HELPER重新编程“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;

(or implement it in a descendant TStrings class if you dislike CLASS HELPERs or don't have them in your Delphi version). (或者如果您不喜欢CLASS HELPERs或者在Delphi版本中没有它们,则在TStrings后代类中实现它)。

This will use a binary search on a sorted TStringList and a sequential search on other TStrings classes. 这将在已排序的TStringList上使用二进制搜索,在其他TStrings类上使用顺序搜索。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM