これらの種類の質問は、一般に、インデックスの保存または計算のどちらかを選択することにつながります。これは、速度とメモリのどちらを選択するかと同義です。また、使用法にもよりますが、これらのFind
ルーチンは頻繁に呼び出されますか?
すでにおっしゃるように、各インデックスを別々の配列に保存すると、あらゆる種類の同期の問題が発生し、メモリが大幅に余分に必要になります。
個人的には、リクエストに応じて各インデックスを計算/取得します。確かに、すべてのアイテムを反復処理するには時間がかかりますが、カウントが10万未満、またはそれ以上の数にとどまる場合は、遅延が発生することはないと確信しています。また、menjarazのコメントのようにデザインをTCollection
作成する場合、削除されたアイテムについて心配する必要はありません。アイテムはありません。
アップデート:
RTTIの使用が必要な任意の名前のプロパティで値を検索するため、この反復タスクは少し遅くなる可能性があります。それを排除するために、私はあなたのためにこの最適化を書きました。これは、プロパティを持たない検索操作の項目を除外することに基づいています。このために、コレクションに含まれるプロパティ名と、それらが属するクラスを格納します。唯一の制限は、重複するプロパティ名が存在しないことですが、とにかく共通の祖先で同じプロパティ名を持つクラスを組み合わせると思います。(ただし、2番目が最初から継承されている限り、同じプロパティ名のクラスを追加することは可能です。)
unit MyCollection;
interface
uses
Classes, TypInfo;
type
TMyItem = class(TCollectionItem)
end;
TMyCollection = class(TOwnedCollection)
private
FPrevItemClass: TClass;
FPropList: TStringList;
function GetItem(Index: Integer): TMyItem;
procedure RegisterItemClass(AClass: TClass);
procedure SetItem(Index: Integer; Value: TMyItem);
protected
procedure Notify(Item: TCollectionItem;
Action: TCollectionNotification); override;
public
constructor Create(AOwner: TPersistent); virtual;
destructor Destroy; override;
function Find(AItem: TMyItem): Boolean; overload;
function Find(const APropName: String; AValue: Variant;
var AItem: TMyItem): Boolean; overload;
property Items[Index: Integer]: TMyItem read GetItem write SetItem;
default;
end;
implementation
resourcestring
SDupPropName = 'Duplicate property name. Only classes with unique ' +
'property names are supposed to be added to this collection.';
{ TMyCollection }
constructor TMyCollection.Create(AOwner: TPersistent);
begin
inherited Create(AOwner, TMyItem);
FPropList := TStringList.Create;
RegisterItemClass(TMyItem);
end;
destructor TMyCollection.Destroy;
begin
FPropList.Free;
inherited Destroy;
end;
function TMyCollection.Find(AItem: TMyItem): Boolean;
var
I: Integer;
begin
for I := 0 to Count - 1 do
if Items[I] = AItem then
begin
Result := True;
Exit;
end;
Result := False;
end;
function TMyCollection.Find(const APropName: String; AValue: Variant;
var AItem: TMyItem): Boolean;
var
I: Integer;
ItemClass: TClass;
begin
Result := False;
if FPropList.Find(APropName, I) then
begin
ItemClass := TClass(FPropList.Objects[I]);
for I := 0 to Count - 1 do
if Items[I] is ItemClass then
if GetPropValue(Items[I], APropName, False) = AValue then
begin
AItem := Items[I];
Result := True;
end;
end;
end;
function TMyCollection.GetItem(Index: Integer): TMyItem;
begin
Result := TMyItem(inherited GetItem(Index));
end;
procedure TMyCollection.Notify(Item: TCollectionItem;
Action: TCollectionNotification);
begin
inherited Notify(Item, Action);
if Action = cnAdded then
if Item.ClassType <> FPrevItemClass then
if FPropList.IndexOfObject(TObject(Item.ClassType)) = -1 then
RegisterItemClass(Item.ClassType)
end;
procedure TMyCollection.RegisterItemClass(AClass: TClass);
var
PropCount: Integer;
PropList: PPropList;
I: Integer;
J: Integer;
PropName: String;
begin
PropCount := GetTypeData(AClass.ClassInfo)^.PropCount;
if PropCount > 0 then
try
GetPropList(AClass.ClassInfo, PropList);
for I := 0 to PropCount - 1 do
begin
PropName := PropList[I].Name;
if not FPropList.Find(PropName, J) then
begin
FPropList.AddObject(PropName, TObject(AClass));
end
else
if not AClass.InheritsFrom(TClass(FPropList.Objects[J])) then
raise EInvalidOperation.Create(SDupPropName);
end;
FPrevItemClass := AClass;
finally
FreeMem(PropList);
end;
end;
procedure TMyCollection.SetItem(Index: Integer; Value: TMyItem);
begin
inherited SetItem(Index, Value);
end;
end.