Как я могу быстрее искать пары имя / значение в Delphi TStringList? - PullRequest
6 голосов
/ 14 сентября 2010

Я реализовал языковой перевод в приложении, поместив все строки во время выполнения в 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;

Новая функция GetTranslation работает в 1000 раз быстрее (на моих образцах записей 2000), чем первая версия.

Ответы [ 5 ]

17 голосов
/ 14 сентября 2010

THashedStringList должно быть лучше, я думаю.

15 голосов
/ 14 сентября 2010

В Delphi 2009 или более поздней версии я бы использовал TDictionary в Generics.Collections. Также обратите внимание, что существуют бесплатные инструменты, такие как http://dxgettext.po.dk/ для перевода приложений.

13 голосов
/ 14 сентября 2010

Если THashedStringList работает для вас, это прекрасно.Его самая большая слабость в том, что каждый раз, когда вы изменяете содержимое списка, хэш-таблица перестраивается.Так что он будет работать для вас, пока ваш список остается небольшим или меняется не очень часто.

Подробнее об этом см .: Слабость THashedStringList , что дает несколько альтернатив.

Если у вас есть большой список, который может быть обновлен, вы можете попробовать GpStringHash от gabr , который не должен пересчитывать всю таблицу при каждомизменить.

4 голосов
/ 14 сентября 2010

Я думаю, что вы неправильно используете EnglishStringList (TStringList).Это отсортированный список, вы добавляете элементы (строки), вы сортируете их, но при поиске вы делаете это с помощью неполной строки (только имя, с IndexOfName ).

Если вы используете IndexOfName в отсортированном списке, TStringList не сможет использовать двухсотомный поиск.Используется последовательный поиск.

(это реализация 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;

Я думаю, что это является причиной низкой производительности.
Альтернативой является использование 2 TStringList:
* Первый(отсортировано) содержит только «Имя» и указатель на второй список, содержащий значение;Вы можете реализовать этот указатель для второго списка, используя «указатель» свойства Object.
* Второй (не отсортированный) список содержит значения.

Когда вы ищете, вы делаете это в первом списке;В этом случае вы можете использовать метод Find .когда вы найдете имя, указатель (реализованный с помощью свойства Object) даст вам позицию во втором списке со значением.

В этом случае метод Find в Sorted List более эффективен, чем HashList (который должен выполнять функцию, чтобы получить позицию значения).

С уважением.

Pd: Извините за ошибки с английским.

2 голосов
/ 08 октября 2010

Вы также можете использовать 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;

(или реализовать ее в классе-потомке TStrings, если вам не нравятся CLASS HELPERs или их нет вваша версия Delphi).

При этом будет использоваться бинарный поиск по отсортированному списку TStringList и последовательный поиск по другим классам TStrings.

...