如何在TList中存储动态数组?

时间:2011-03-03 22:58:32

标签: delphi delphi-7

我需要存储未知数量的群组。每个组都有未知数量的元素/项目。 这是我的'小组':

 TGroup= array of Integer;     <------ dynamic array (as you can see) :)

我想使用TList来保存我的群组。我的想法是,我可能希望稍后访问这些组并向其添加更多项目。

我有这段代码,但我不能让它运作:

TYPE
   TGroup= array of Integer;                              // Each group has x items (x can be from 1 to 10000)


procedure TForm1.FormCreate(Sender: TObject);
VAR CurGroup: TGroup;
    grp, item: Integer;
    Groups: TList;                                        // can contain up to 1 million groups
begin
 Groups:= TList.Create;

 { Init }
 for grp:= 1 to 4  DO                                     // Put a dummy item in TList
  begin
   SetLength(CurGroup, 1);                                // Create new group
   Groups.Add(@CurGroup);                                 // Store it
  end;

 CurGroup:= NIL;                                          // Prepare for next use

 for grp:= 1 to 4  DO                                     // We create 4 groups. Each group has 3 items
  begin
    CurGroup:= Groups[Groups.Count-1];                    // We retrieve the current group from list in order to add more items to it

    { We add few items }
    for item:= 0 to 2  DO
     begin
       SetLength(CurGroup, Length(CurGroup)+1);           // reserve space for each new item added
       CurGroup[item]:= Item;
     end;

    Groups[Groups.Count-1]:= @CurGroup;                   // We put the group back into the list
  end;

 { Verify }
 CurGroup:= NIL;
 CurGroup:= Groups[0];
 Assert(Length(CurGroup)> 0);                             // FAIL
 if  (CurGroup[0]= 0)
 AND (CurGroup[1]= 1)
 AND (CurGroup[2]= 2)
 then Application.ProcessMessages;                        

 FreeAndNil(Groups);
end;

注意:代码已完成。您可以将它粘贴到Delphi(7)中进行试用。

6 个答案:

答案 0 :(得分:6)

哦,在新版本的Delphi中,这会好得多......你会使用泛型,TList&lt; T&gt;。 var组:TList&lt; TGroup&gt ;;

你最好的选择是使用另一个动态数组:组:TGroup数组;

原因是动态数组是编译器管理和引用计数的。 TList仅适用于指针。当试图将dynarrays放入TList时,没有直接的方法来保持引用计数的正确。

您遇到的另一个问题是您将动态数组变量的堆栈地址添加到TList,而不是实际的数组。 @CurGroup表达式是“CurGroup变量的地址”,它是一个局部变量,位于堆栈上。

答案 1 :(得分:4)

我在这台机器上没有D7,但你可能会尝试这样的东西(控制台测试应用程序 - 它在XE中编译时没有提示或警告,但不确定D7将如何处理它):< / p>

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes;

type
  TGroup = array of Integer;
  THolder=class(TObject)
    Group: TGroup;
  end;

var
  GroupList: TList;
  i: Integer;

begin
  GroupList := TList.Create;
  for i := 0 to 2 do
  begin
    GroupList.Add(THolder.Create);
    with THolder(GroupList[GroupList.Count - 1]) do
    begin
      SetLength(Group, 3);
      Group[0] := i;
      Group[1] := i + 10;
      Group[2] := i + 20;
    end;
  end;
  for i := 0 to GroupList.Count - 1 do
  begin
    Writeln(i, ':0 ', THolder(GroupList[i]).Group[0]);
    Writeln(i, ':1 ', THolder(GroupList[i]).Group[1]);
    Writeln(i, ':2 ', THolder(GroupList[i]).Group[2]);
  end;
  // Not cleaning up list content, because we're exiting the app.
  // In a real app, of course, you'd want to free each THolder in 
  // the list, or use a TObjectList and let it own the objects.
  // You'd also want a try..finally to make sure the list gets freed.
  GroupList.Free;
  Readln;
end.

答案 2 :(得分:4)

您可能想要尝试的另一件事是使用TObjectList。 TObjectList能够存储对象列表,因此您可以创建TObject的新类后代,然后使用这些对象管理TObjectList。

像(未经测试)的东西:

type TGroupArray : array of Integer;

type TGroup = class(Tobject)
  GroupArray : TGroupArray;
end;

GroupList : TobjectList;

procedure TForm1.FormCreate(Sender: TObject);                                        
var CurGroup : TGroup;
begin  

GroupList:= TObjectList.Create; 
CurGroup := TGroup.Create;
SetLength(CurGroup.GroupArray,1);
CurGroup.GroupArray[0] := 10;

GroupList.Add(CurGroup);

RetreiveGroup := GroupList.Items[0];
FreeandNil(GroupList);
end;

等...

答案 3 :(得分:4)

我已经创建了一个围绕动态数组RTTI的包装器。

这只是初稿,但它受到了您的问题的启发,以及缺少TList方法的事实。

type
  TGroup: array of integer;

var 
  Group: TGroup;
  GroupA: TDynArray;
  i, v: integer;
begin
  GroupA.Init(TypeInfo(TGroup),Group); // associate GroupA with Group
  for i := 0 to 1000 do begin
    v := i+1000; // need argument passed as a const variable
    GroupA.Add(v);
  end;
  v := 1500;
  if GroupA.IndexOf(v)<0 then // search by content
    ShowMessage('Error: 1500 not found!');
  for i := GroupA.Count-1 downto 0 do
    if i and 3=0 then
      GroupA.Delete(i); // delete integer at index i
end;

这个TDynArray包装器也可以用于字符串数组或记录数组...记录只需要打包并且只能引用计数字段(byte,integer,double ...)或字符串引用 - 字段(没有Variant或Interface in。)

IndexOf()方法将按内容进行搜索。那是例如对于记录数组,所有记录字段(包括字符串)必须匹配。

请参阅我们的源代码存储库中的the SynCommons.pas unit中的TDynArray。适用于Delphi 6到XE,并处理Unicode字符串。

TTestLowLevelCommon._TDynArray方法是与此包装器关联的自动单一测试。您将在此处找到记录数组和更高级功能的样本。

我目前正在实施SaveToStreamLoadToStream方法......

也许是在所有Delphi版本中使用类通用功能的新方法。

修改:

我在TDynArray记录/对象中添加了一些新方法:

  • 现在您可以将动态数组内容保存到字符串或从字符串加载(使用LoadFromStream/SaveToStreamLoadFrom/SaveTo方法) - 它将使用专有但非常快速的二进制流布局;
  • 并且您可以通过两种方式对动态数组内容进行排序:就地(即交换数组元素内容)或通过外部整数索引查找数组(使用CreateOrderedIndex方法 - 在此例如,您可以对同一数据进行多次订购);
  • 您可以指定任何自定义比较函数,并且有一个新的Find方法可以使用快速二进制搜索(如果可用)。

以下是这些新方法的工作原理:

var
  Test: RawByteString;
...
  Test := GroupA.SaveTo;
  GroupA.Clear;
  GroupA.LoadFrom(Test);
  GroupA.Compare := SortDynArrayInteger;
  GroupA.Sort;
  for i := 1 to GroupA.Count-1 do
    if Group[i]<Group[i-1] then
      ShowMessage('Error: unsorted!');
  v := 1500;
  if GroupA.Find(v)<0 then // fast binary search
    ShowMessage('Error: 1500 not found!');

更接近通用范例,更快,对于Delphi 6而言更接近XE ......

答案 4 :(得分:1)

编码:

 for grp:= 1 to 4  DO                                     // Put a dummy item in TList
  begin
   SetLength(CurGroup, 1);                                // Create new group
   Groups.Add(@CurGroup);                                 // Store it
  end;

实际上,SetLength(CurGroup)不会创建新组。 它将调整现有CurGroup中唯一的一个。

所以@CurGroup不会改变:它总是在堆栈上的某个地址,其中添加了CurGroup。您可以多次将相同的地址添加到列表中。

所以你必须创建一个TGroup实例的动态数组,如:

var GroupArray: array of TGroup;

SetLength(GroupArray,4);
for grp := 0 to high(GroupArray) do
begin
  SetLength(GroupArray[grp],1);
  Groups.Add(@GroupArray[grp]);
end;

但是,当然,GroupArray必须在群组需要的所有时间内保持分配状态。因此,您可能将此GroupArray作为属性放在类中,因为如果在堆栈上创建此GroupArray,则当方法退出并释放其堆栈时,将释放所有GroupArray []项。

但是,当然,GroupArray []将更直接访问TGroup项目... Groups [i]将等于GroupArray [] ...没有引用计数问题...因为例如如果你从Groups []中的指针调整一个TGroup项目,我不确定你是否会有任何内存泄漏......

答案 5 :(得分:0)

所以,基本上每个人建议创建一个数组数组而不是使用TList。好吧,我已经做到了。我只是想从'数组数组'“升级”到TList,因为它有Add()。看起来我将返回原始代码。 谢谢大家,每个答案+1。