Как хранить динамические массивы в TList?
мне нужно сохранить неизвестное количество групп. Каждая группа имеет неизвестное количество элементов / элементов. Это моя "группа":
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 ответов
Я создал оболочку вокруг динамического массива 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
wrapper будет работать также с массивом строк или массивом записей... Записи должны быть только упакованы и иметь только не ссылочные подсчитанные поля (byte, integer, double...) или строковые поля, подсчитанные по ссылкам (без варианта или интерфейса внутри).
в Метод IndexOf () будет искать по контенту. Например, для массива записей все поля записи (включая строки) должны совпадать.
посмотреть TDynArray
на в SynCommons.pas unit из нашего репозитория исходного кода. Работает от Delphi 6 до XE и обрабатывает строки Unicode.
и TTestLowLevelCommon._TDynArray
метод-это автоматизированные унитарные тесты, связанные с этой оболочкой. Вы найдете здесь образцы массива записей и более продвинутые особенности.
в настоящее время я реализую SaveToStream
и LoadToStream
методы...
возможно, новый способ использования универсальных функций во всех версиях Delphi.
Edit:
я добавил несколько новых методов к 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...
О, это было бы намного приятнее в новых версиях Delphi... Вы бы использовали общий, TList
лучше всего использовать другой динамический массив: группы: массив TGroup;
причина в том, что динамические массивы управляются компилятором и подсчитываются ссылки. TList работает только с указателями. Нет прямого способа сохранить ссылочные отсчеты в здравом уме при попытке поместить dynarrays в TList.
другой проблема в том, что вы добавляете адреса стека переменной динамического массива в TList, а не фактический массив. Выражение @CurGroup - это "адрес переменной CurGroup", которая является локальной переменной, находится в стеке.
У меня нет D7 на машине здесь, но вы можете попробовать что - то вроде этого (консольное тестовое приложение-оно компилируется в XE без подсказок или предупреждений, но не уверен, как D7 будет обрабатывать его):
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.
еще одна вещь, которую вы можете попробовать, - это использовать 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;
etc...
когда вы код:
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... Группы[i] будут равны GroupArray []... Без проблемы подсчета ссылок... Так, например, при изменении размера, например, TGroup элемента из Указателя в группах [] я не уверен, что у вас не будет утечки памяти...
Так, в основном все предлагает создать массив массива вместо использования TList. Ну, я уже сделал это. Я просто хотел "обновить" от "массива массива" до TList, так как он имеет Add(). Похоже, я вернусь к исходному коду. Спасибо всем и +1 за каждый ответ.