Лучший способ изменить размер TStringList?

Я часто нахожу, что мне нужно "изменить размер" a TStringList чтобы удерживать ровно N элементов, либо добавляя дополнительные пустые строки в список, либо удаляя ненужные.

на контейнере STL C++ я мог бы использовать resize метод, но поскольку этого, похоже, не существует, я обычно делаю что-то вроде этого (предупреждение: псевдокод!).

list.beginUpdate;

while list.Count < requiredSize do
begin
   list.add('');
end;

while list.Count > requiredSize do
begin
   list.delete(list.count-1);
end;

list.endUpdate;

есть гораздо более простой способ сделать это, что я проглядел?

4 ответов


судя по реализации TStringList.Assign, нет лучшего способа сделать это. Они в основном называют Clear и добавьте строки один за другим.

вы, конечно, должны поместить свой код в метод утилиты:

procedure ResizeStringList(List : TStrings; ANewSize: Integer);
begin
...
end;

или вы можете использовать помощник класса, чтобы ваш метод казался частью .


метод в вашем вопросе-это лучшее, что вы можете сделать. Вы можете сделать его чище, если используете помощник класса. Например:

type
  TStringsHelper = class helper for TStrings
    procedure SetCount(Value: Integer);
  end;

procedure TStringsHelper.SetCount(Value: Integer);
begin
  BeginUpdate;
  try
    while Count<Value do
      Add('');
    while Count>Value do
      Delete(Count-1);
  finally
    EndUpdate;
  end;
end;

и тогда вы можете написать:

List.SetCount(requiredSize);

на Capacity свойство почти идеально, потому что оно выделит правильное количество записей во внутреннем массиве. Тем не менее, у него есть печальные недостатки, которые:

  • вновь выделенная память не инициализируется.
  • количество элементов Strings.Count не обновляется.

поскольку архитектура компонента Delphi относится к базовому типу TStrings, вы можете предоставить свой конкретный подкласс, который может поддерживать более эффективный изменение функциональности. Е. Г. рассмотрим следующую реализацию TList.SetCount.

procedure TList.SetCount(NewCount: Integer);
var
  I: Integer;
begin
  if (NewCount < 0) or (NewCount > MaxListSize) then
    Error(@SListCountError, NewCount);
  if NewCount > FCapacity then
    SetCapacity(NewCount);
  if NewCount > FCount then
    FillChar(FList^[FCount], (NewCount - FCount) * SizeOf(Pointer), 0)
  else
    for I := FCount - 1 downto NewCount do
      Delete(I);
  FCount := NewCount;
end;

после обновления емкости, если есть вновь выделенная память, она инициализируется с помощью FillChar. Это намного эффективнее, чем добавление / удаление элементов по одному за раз.

таким образом, вы можете либо предоставить свою собственную независимую конкретную реализацию TStrings подкласс, или просто сделайте копию Delphi's TStringList который включает в себя соответствующий SetCount метод.

тем не менее, я считаю маловероятным, что этот раздел кода будет испытывать какие-либо проблемы с производительностью, поэтому вашего собственного решения, завернутого в соответствующие методы утилиты, будет достаточно. Дэвид также хорошо, хотя лично я не считаю, что функция "помощник класса" настолько полезна. "Старый способ" реализации помощников классов гораздо более универсален.


var
    List:  TStringList;

Assert(requiredSize >= 0);
if requiredSize > List.Count then
    List.Capacity := requiredSize
else
    while List.Count > requiredSize do
        List.Delete(List.Count - 1);