Общий список записей с подсписком?
Я хочу использовать общий TList записей с подсписком в Delphi XE5:
type
TMyRecord=record
Value1: Real;
SubList: TList<Integer>;
end;
TMyListOfRecords=TList<TMyRecord>;
var
MyListOfRecords: TMyListOfRecords;
Назначения в поле записей не возможны:
MyListOfRecords[0].Value1:=2.24;
или же
MyListOfRecords[0].SubList:=TList<Integer>.Create;
приведет к ошибке "левая сторона не может быть назначена" компилятором.
Смотрите также: Как изменить значение TList
Следующий обходной путь работает:
AMyRecord:=MyListOfRecords[0];
AMyRecord.Value1:=2.24;
AMyRecord.SubList:=TList<Integer>.Create;
AMyRecord.SubList.Add(33);
MyListOfRecords[0]:=AMyRecord;
Из-за проблем с производительностью я хотел бы избежать копирования данных во временную запись AMyrecord. Я бы предпочел получить доступ к полям записи и подсписку напрямую.
Каков наилучший способ справиться с этим?
1 ответ
Список предоставляет свое внутреннее хранилище, которое представляет собой динамический массив, через List
имущество. Таким образом, вы можете написать:
MyListOfRecords.List[0].Value1 := 2.24;
Имеет ли это какое-то измеримое различие в производительности по сравнению с альтернативой с ценными копиями, я не могу сказать. Стоит проверить это.
Как правильно говорит @LURD, List
возвращает внутреннее хранилище. И это может иметь больше, чем Count
элементы. Конкретно это имеет Capacity
элементы. Таким образом, если вы используете его, вы должны получить доступ к элементам с помощью индексации массива поверх элементов 0
в Count-1
, Помните также, что изменения размера списка могут включать перераспределение, и поэтому внутреннее хранилище может перемещаться. Любая ссылка на которую вы берете List
действует только до следующего перераспределения.
Эти предупреждения должны указывать на то, что вы планируете использовать только List
если этого требуют ограничения производительности. И даже тогда, используйте это экономно.
В моей кодовой базе у меня есть альтернатива TList<T>
чья Items[]
Свойство возвращает указатель на элемент. Контейнер по-прежнему хранится в виде динамического массива для эффективного размещения памяти. Я предпочел этот вариант List
собственность, потому что я чувствовал, что это привело к более чистому коду.
Хорошо, вы попросили взглянуть на мой класс списка, который возвращает указатели на элементы. Вот:
type
TReferenceList<T> = class(TBaseValueList<T>)
type
P = ^T;
private
function GetItem(Index: Integer): P;
public
property Items[Index: Integer]: P read GetItem; default;
public
// .... helper types for enumerators excised
public
function GetEnumerator: TEnumerator;
function Enumerator(Forwards: Boolean): TEnumeratorFactory;
function ReverseEnumerator: TEnumeratorFactory;
function IndexedEnumerator: TIndexedEnumeratorFactory;
end;
Теперь нужно какое-то объяснение. Базовый класс, TBaseValueList<T>
моя альтернатива TList<T>
, Вы могли бы заменить TList<T>
если хочешь. Я не знаю, потому что мой базовый класс не имеет Items
имущество. Это потому, что я хочу, чтобы специализированные классы представили это. Моя другая специализация:
type
TValueList<T> = class(TBaseValueList<T>)
private
function GetItem(Index: Integer): T;
procedure SetItem(Index: Integer; const Value: T);
public
property Items[Index: Integer]: T read GetItem write SetItem; default;
end;
Реализация моего TBaseValueList<T>
довольно очевидно. Это очень похоже на TList<T>
, Я не думаю, что вам действительно нужно видеть какую-либо реализацию. Это все очень очевидно.
Как простой способ получить ссылку на элемент, вы можете обернуть List
так:
type
TMyList<T> = class(TList<T>)
public
type
P = ^T;
private
function GetRef(Index: Integer): P;
public
property Ref[Index: Integer]: P read GetRef;
end;
function TMyList<T>.GetRef(Index: Integer): P;
begin
Result := @List[Index];
end;
Если вам нужен более богатый набор контейнеров, чем в Delphi, вам стоит взглянуть на Spring4D. Хотя я не уверен, есть ли у них что-то вроде моего контейнера, который возвращает ссылки.