TQueue.Capacity属性的目的或功能是什么?

Delphi 的泛型TQueue类有一个属性,叫做Capacity.如果其中的项目数TQueue超过其容量,额外的项目仍会添加到队列中。文档说该属性“获取或设置队列容量,即不调整大小的队列的最大大小。” 听起来队列有点像固定长度的数组(内存方面)——直到它满了,此时它变得更像是一个动态数组?那是准确的吗?

程序员什么时候想要或需要获取或设置 TQueue 的容量?

回答

理论

考虑以下示例,它生成一个动态随机整数数组:

program DynArrAlloc;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Windows, System.SysUtils;

const
  N = 100000000;

var
  a: TArray<Integer>;
  i: Integer;
  tc1, tc2: Cardinal;

begin

  tc1 := GetTickCount;

  SetLength(a, 0);
  for i := 1 to N do
  begin
    SetLength(a, Succ(Length(a)));
    a[High(a)] := Random(1000);
  end;

  tc2 := GetTickCount;
  Writeln(tc2 - tc1);
  Readln;

end.

在我的系统上,运行它需要 4.5 秒。

请注意,我 - 在每次迭代中 - 重新分配数组,以便它可以再容纳一个项目。

如果我从一开始就分配了一个足够大的数组会更好:

program DynArrAlloc;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Windows, System.SysUtils;

const
  N = 100000000;

var
  a: TArray<Integer>;
  i: Integer;
  tc1, tc2: Cardinal;

begin

  tc1 := GetTickCount;

  SetLength(a, N);
  for i := 1 to N do
    a[N - 1] := Random(1000);

  tc2 := GetTickCount;
  Writeln(tc2 - tc1);
  Readln;

end.

这一次,程序只需要 0.6 秒。

因此,应该始终尽量不要不必要地重新分配。每次在第一个例子中重新分配,我都需要请求更多的内存;然后我需要将数组复制到新位置,最后释放旧内存。显然,这是非常低效的。

不幸的是,在开始时分配足够大的数组并不总是可能的。您可能根本不知道最终的元素数。

一种常见的策略是分步分配:当数组已满并且您还需要一个插槽时,再分配几个插槽,但要跟踪实际使用的插槽数:

program DynArrAlloc;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Windows, System.SysUtils;

const
  N = 100000000;

var
  a: TArray<Integer>;
  i: Integer;
  tc1, tc2: Cardinal;
  ActualLength: Integer;

const
  AllocStep = 1024;

begin

  tc1 := GetTickCount;

  SetLength(a, AllocStep);
  ActualLength := 0;
  for i := 1 to N do
  begin
    if ActualLength = Length(a) then
      SetLength(a, Length(a) + AllocStep);
    a[ActualLength] := Random(1000);
    Inc(ActualLength);
  end;

  // Trim the excess:
  SetLength(a, ActualLength);

  tc2 := GetTickCount;
  Writeln(tc2 - tc1);
  Readln;

end.

现在我们需要 1.3 秒。

在这个例子中,我分配了固定大小的块。更常见的策略可能是在每次重新分配时将数组加倍(或乘以 1.5 或其他)或以巧妙的方式组合这些选项。

应用理论

引擎盖下,TList<T>TQueue<T>TStack<T>TStringList等需要为无限数目的项目的动态分配的空间。为了提高性能,这些类确实分配了不必要的资源。该Capacity是你能适应在当前分配的内存,而元件的数量Count <= Capacity是在容器中的元素的实际数量。

您可以设置该Capacity属性以减少填充容器时对中间分配的需求,并且您从一开始知道元素的最终数量:

var
  L: TList<Integer>;

begin

  L := TList<Integer>.Create;
  try
    while not Something.EOF do
      L.Add(Something.GetNextValue);
  finally
    L.Free;
  end;

没问题,可能只需要一些重新分配,但是

  L := TList<Integer>.Create;
  try
    L.Capacity := Something.Count;
    while not Something.EOF do
      L.Add(Something.GetNextValue);
  finally
    L.Free;
  end;

会更快,因为会有任何中间重新分配。


以上是TQueue.Capacity属性的目的或功能是什么?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>