Generic TList entries with sub-list?

I want to use a common TList of entries with an optional list in Delphi XE5:

type TMyRecord=record Value1: Real; SubList: TList<Integer>; end; TMyListOfRecords=TList<TMyRecord>; var MyListOfRecords: TMyListOfRecords; 

Assignments to the record field are not possible:

 MyListOfRecords[0].Value1:=2.24; 

or

 MyListOfRecords[0].SubList:=TList<Integer>.Create; 

will cause the compiler to fail to assign the "left side" to the error.

See also: How to change TList <record> value?

The following workaround works:

 AMyRecord:=MyListOfRecords[0]; AMyRecord.Value1:=2.24; AMyRecord.SubList:=TList<Integer>.Create; AMyRecord.SubList.Add(33); MyListOfRecords[0]:=AMyRecord; 

Due to performance issues, I would like to avoid copying data to a temporary AMyrecord. I would prefer to access the fields of records and subscriptions directly.

What is the best way to handle this?

+5
source share
1 answer

The list shows its internal storage, which is a dynamic array, through the List property. Therefore you can write:

 MyListOfRecords.List[0].Value1 := 2.24; 

Whether this is any measurable difference in performance compared to the alternative with copy values, I cannot say. It would be worth checking this out.

As @LURD correctly says, List returns internal storage. And it can have more than Count elements. In particular, it has elements of Capacity . Thus, if you use it, you must access the elements by indexing the array, over the elements 0 - Count-1 . Remember also that resizing a list can include redistribution, so internal storage can be moved. Any link you take to List is valid only until the next redistribution.

These warnings should suggest that you are considering using List if performance constraints require it. And even then use it sparingly.

In my code base, I have an alternative to TList<T> , the Items[] property returns a pointer to an element. The container still stores a dynamic array for efficient memory layout. I preferred this option for the List property, because I felt that this led to the creation of cleaner code.


OK, you asked for a look at my list class, which returns pointers to elements. Here he is:

 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; 

Now some explanation is needed. The base class TBaseValueList<T> is my alternative to TList<T> . You can replace TList<T> if you want. I am not because my base class does not have an Items property. This is because I want specialized classes to represent it. Other specialization:

 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; 

The implementation of my TBaseValueList<T> pretty obvious. This is very similar to TList<T> . I don't think you really need to see any implementation. All this is very obvious.

As an easy way to get a link to an element, you can wrap a List like this:

 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; 

If you need a richer set of containers than provided by Delphi, you might need to check out Spring4D. Although I'm not sure that they have something like my container that returns links.

+6
source

All Articles