Can I pass in one function for TObjectList.IndexOf, and another function for TObjectList.Sort?
Summarization:
TList.IndexOf (TList defined in the unit Classes.pas) iterates linearly through the contained items, and compares the reference. TList.IndexOf (TList defined in the unit Generics.Collections.pas) also iterates linearly through the contained items, but uses a comparer to compare whether the items are equal.
Both TList.Sort and TList.Sort can use a comparer.
=================================================
For an instance of the TForceList type defined in the following unit, I could use
instance.Sort(@ForceCompare);
to QuickSort it using its Value field as sorting criteria. However, when I call
instance.IndexOf(AnotherInstance)
I want to use its ElementZ field as comparing criteria, i.e., the ForceEqual
function. I am wondering how can I achieve this?
PS: If the generics collection is used, I guess I could use
TList<TForce>.Create(TComparer<TForce>.Construct(ForceEqual));
The unit:
unit uChemParserCommonForce;
interface
uses
uMathVector3D,
Contnrs;
type
TForce = class;
TForceList = class;
TForce = class
private
FElementZ: Integer;
FValue: TVector3D;
public
property ElementZ: Integer read FElementZ;
property Value: TVector3D read FValue;
constructor Create(aElementZ: Integer; aX, aY, aZ: Double);
function ToString(): string; {$IF DEFINED(FPC) OR DEFINED(VER210)} override; {$IFEND}
end;
// Mastering Delphi 6 - Chapter 5 -
TForceList = class(TObjectList)
protected
procedure SetObject(Index: Integer; Item: TForce);
function GetObject(Index: Integer): TForce;
public
function Add(Obj: TForce): Integer;
procedure Insert(Index: Integer; Obj: TForce);
property Objects[Index: Integer]: TForce read GetObject
write SetObject; default;
end;
function ForceCompare(Item1, Item2: Pointer): Integer;
function ForceEqual(Item1, Item2: Pointer): Boolean;
implementation
uses
Math, SysUtils;
function ForceCompare(Item1, Item2: Pointer): Integer;
begin
// Ascendent
开发者_JS百科 // Result := CompareValue(TForce(Item1).Value.Len, TForce(Item2).Value.Len);
// Descendent
Result := CompareValue(TForce(Item2).Value.Len, TForce(Item1).Value.Len);
end;
function ForceEqual(Item1, Item2: Pointer): Boolean;
begin
Result := TForce(Item1).ElementZ = TForce(Item2).ElementZ;
end;
constructor TForce.Create(aElementZ: Integer; aX, aY, aZ: Double);
begin
FElementZ := aElementZ;
FValue := TVector3D.Create(aX, aY, aZ);
end;
function TForce.ToString: string;
begin
Result := IntToStr(FElementZ) + ' X: ' + FloatToStr(FValue.X) + ' Y: ' +
FloatToStr(FValue.Y) + ' Z: ' + FloatToStr(FValue.Z);
end;
{ TForceList }
function TForceList.Add(Obj: TForce): Integer;
begin
Result := inherited Add(Obj);
end;
procedure TForceList.SetObject(Index: Integer; Item: TForce);
begin
inherited SetItem(Index, Item);
end;
function TForceList.GetObject(Index: Integer): TForce;
begin
Result := inherited GetItem(Index) as TForce;
end;
procedure TForceList.Insert(Index: Integer; Obj: TForce);
begin
inherited Insert(Index, Obj);
end;
end.
The non-generic TObjectList
uses TList.IndexOf
, which simply iterates through the internal array and compares pointers.
Likewise, the generic TObjectList<T>
uses TList<T>.IndexOf
, which uses an IComparer
. TList<T>.Sort
uses TArray.Sort<T>
passing in whatever IComparer
was assigned at the list's creation.
The comparer is private and only assigned in the list constructor so I don't see an easy way to override this behavior.
Update
TList<T>
provides and overloaded Sort
that accepts a comparer as an argument, without modifying the private comparer. So you can sort using one comparer and indexof can use a different one.
The entire idea behind the Sort method is it really sorts the list... in other words, after calling the sort method, the physical order of the elements on the list is changed to meet the sort criteria.
The IndexOf method, as you can see in your own Delphi RTL code, is just a linear search by reference returning the physical index of the first matching element.
The index returned can be used to retrieve the object on the list, like this:
SomeIndex := AList.IndexOf(SomeObject);
//more code...
//you can re-use the reference...
//and maybe more...
SomeObject := AList[SomeIndex];
You'll see why, the IndexOf method shall not return a index based on a different criteria than the physical order of the list... and it happens if you call Sort first, the physical order is reflecting the passed sort criteria.
That said, it looks like you may want to
- maintain two different lists, sorted with different criteria and use one or another when appropriate.
- re-sort the list based on the applicable criteria for the operation your application is processing at a given time.
What is more performant depends on how your application use those objects, the amount of data it is processing and even the memory available to your process at runtime.
Not with the standard TObjectList. It (actually the base TList) is written to support a custom sort function using CustomSort, but there's no such provision for a custom IndexOf. Of course, you could write your own implementation of something that works that way.
精彩评论