我需要存储未知数量的群组。每个组都有未知数量的元素/项目。 这是我的'小组':
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)中进行试用。
答案 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
方法是与此包装器关联的自动单一测试。您将在此处找到记录数组和更高级功能的样本。
我目前正在实施SaveToStream
和LoadToStream
方法......
也许是在所有Delphi版本中使用类通用功能的新方法。
修改:
我在TDynArray
记录/对象中添加了一些新方法:
LoadFromStream/SaveToStream
或LoadFrom/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。