将一个元素附加到动态数组

时间:2011-04-22 13:39:30

标签: delphi

在我的代码中,这是一种非常频繁的模式:

SetLength(SomeDynamicArray, Length(SomeDynamicArray)+1);
SomeDynamicArray[High(SomeDynamicArray)] := NewElement;

有没有办法在一行中做到这一点?

编辑:这非常低效。我知道。我使用动态数组(在我自己的代码中,在我个人的项目中,我只使用),因为它们最容易使用,我只需要尽可能少的代码完成任务。

8 个答案:

答案 0 :(得分:20)

这是一个带有泛型的黑客攻击,只适用于TArray<T>

type
  TAppender<T> = class
    class procedure Append(var Arr: TArray<T>; Value: T);
  end;

class procedure TAppender<T>.Append;
begin
  SetLength(Arr, Length(Arr)+1);
  Arr[High(Arr)] := Value;
end;

用法:

var
  TestArray: TArray<Integer>;

begin
  TAppender<Integer>.Append(TestArray, 5);
end.

答案 1 :(得分:11)

每次调用SetLength时,内存都会被重新分配。也许整个阵列需要复制到不同的位置。而你只想在阵列中添加一个元素!

基本上:永远不要这样做。它有两种方法。最简单的情况是,如果您事先知道数组的最大大小:

procedure Example1;
var
  data: array of string;
  ActualLength: integer;

  procedure AddElement(const Str: string);
  begin
    data[ActualLength] := Str;
    inc(ActualLength);
  end;

begin

  ActualLength := 0;
  SetLength(data, KNOWN_UPPER_BOUND);

  for ...
    while ...
      repeat ...
        AddElement(SomeString);

  SetLength(data, ActualLength);

end;

Here是这种方法的一个实际例子。

如果您不知道任何上限先验,则以大块分配:

procedure Example2;
const
  ALLOC_BY = 1024;
var
  data: array of string;
  ActualLength: integer;

  procedure AddElement(const Str: string);
  begin
    if ActualLength = length(data) then
      SetLength(data, length(data) + ALLOC_BY);

    data[ActualLength] := Str;
    inc(ActualLength);
  end;

begin

  ActualLength := 0;
  SetLength(data, ALLOC_BY);

  for ...
    while ...
      repeat ...
        AddElement(SomeString);

  SetLength(data, ActualLength);

end;

第二种方法是在运行时库的TList<T>TObjectList<T>TStringList等中实现的。因此,当您使用这些类时,将列表附加到列表中就可以了。项目一次。

答案 2 :(得分:4)

这是一种导致内存碎片化的反模式。而是使用Generics.Collections.TList<T>并调用Add方法添加新项目。

没有一个衬垫可以扩展阵列并添加一个项目。您可以使用泛型创建自己的动态数组包装器,以便在您愿意的情况下执行此操作。基本上就是Generics.Collections.TList<T>是什么。

答案 3 :(得分:4)

从Delphi XE7开始,您可以执行以下操作:

SomeDynamicArray := SomeDynamicArray + [NewElement];

ref:Marco Tech Blog,2014年9月18日:Dynamic Arrays in Delphi XE7

答案 4 :(得分:3)

有一个方便的插入程序,你可以像这样使用:

Insert([NewElement], SomeDynamicArray, High(SomeDynamicArray));

答案 5 :(得分:2)

如果你有Delphi 2009或更高版本,并且你真的想缩短上面的代码,你可以尝试类似的东西

type
  DataArray<T> = record
    Data: array of T;
    procedure Append(const Value: T);
    function Count: integer;
  end;


{ DataArray<T> }

procedure DataArray<T>.Append(const Value: T);
begin
  SetLength(Data, length(Data) + 1);
  Data[high(Data)] := Value;
end;

function DataArray<T>.Count: integer;
begin
  result := length(Data);
end;

然后你可以做

procedure TForm1.FormCreate(Sender: TObject);
var
  data: DataArray<string>;
begin
  data.Append('Alpha');
  data.Append('Beta');
  Caption := IntToStr(data.Count) + ': ' data.Data[0] + ' & ' + data.Data[1];
end;

答案 6 :(得分:2)

MyList.Add(myobject);

IMO动态数组只应在编译时使用,你不知道数组的确切大小,但在运行时你会知道。

如果你需要不断地操作你的数组大小,你不应该使用数组而是TList或其后代之一,正如其他人所提到的:TObjectList,TInterfaceList,TStringList.Objects []可以所有都被使用(和滥用),也有一些新的,TList为原始类型。在将泛型引入Delphi之前,TList曾经是一种痛苦 - 你必须使用指针 - 但是使用泛型:TList&lt; T&gt;这很容易。

另外,使用你创建的任何列表的capacity属性 - 它将预先分配给定的内存量,这样你的代码就不会导致大量内存在你每次执行操作时都被搞砸了在你的清单上。 (如果超出了您分配的容量,内存管理器将在运行时为您提供更多内存 - 您不会失败 - 请参阅Delphi帮助)

答案 7 :(得分:-1)

您可以简单地使用此代码将元素附加到任何动态数组的末尾。快速而简短。

...
var
  myArray: array of Integer;
  element: Integer;
...
Insert(element, myArray, MaxInt);
...