Могу ли я передать одну функцию для TObjectList.IndexOf и другую функцию для TObjectList.Sort?
Суммирование:
TList.IndexOf (TList, определенный в модуле Classes.pas) выполняет линейную итерацию по содержащимся элементам и сравнивает ссылку. TList.IndexOf (TList, определенный в модуле Generics.Collections.pas), также выполняет линейную итерацию по содержащимся элементам, но использует компаратор для сравнения, равны ли элементы.
И TList.Sort, и TList.Sort могут использовать компаратор.
=================================================
Для экземпляра типа TForceList, определенного в следующем блоке, я мог бы использовать
instance.Sort(@ForceCompare);
Быстрая сортировка с использованием поля "Значение" в качестве критерия сортировки. Тем не менее, когда я звоню
instance.IndexOf(AnotherInstance)
Я хочу использовать его поле ElementZ в качестве критерия сравнения, т.е. ForceEqual
функция. Мне интересно, как я могу этого достичь?
PS: если бы использовалась коллекция дженериков, думаю, я мог бы использовать
TList<TForce>.Create(TComparer<TForce>.Construct(ForceEqual));
Единица:
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
// 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.
3 ответа
Неуниверсальный TObjectList
использования TList.IndexOf
, который просто перебирает внутренний массив и сравнивает указатели.
Аналогично, универсальный TObjectList<T>
использования TList<T>.IndexOf
, который использует IComparer
, TList<T>.Sort
использования TArray.Sort<T>
проходя во что угодно IComparer
был назначен при создании списка.
Компаратор является закрытым и назначается только в конструкторе списка, поэтому я не вижу простого способа переопределить это поведение.
Обновить
TList<T>
обеспечивает и перегружен Sort
который принимает в качестве аргумента компаратор без изменения приватного компаратора. Таким образом, вы можете сортировать, используя один компаратор, а indexof может использовать другой.
Вся идея метода Sort заключается в том, что он действительно сортирует список... другими словами, после вызова метода sort физический порядок элементов в списке изменяется в соответствии с критериями сортировки.
Метод IndexOf, как вы можете видеть в своем собственном коде RTL Delphi, - это просто линейный поиск по ссылке, возвращающей физический индекс первого соответствующего элемента.
Возвращенный индекс может быть использован для извлечения объекта из списка, например так:
SomeIndex := AList.IndexOf(SomeObject);
//more code...
//you can re-use the reference...
//and maybe more...
SomeObject := AList[SomeIndex];
Вы поймете, почему метод IndexOf не должен возвращать индекс, основанный на критериях, отличных от физического порядка в списке... и если вы сначала вызываете Sort, физический порядок отражает переданные критерии сортировки.
Тем не менее, похоже, что вы можете
- вести два разных списка, отсортированных по разным критериям и использовать тот или иной в случае необходимости.
- пересортируйте список на основе применимых критериев для операции, которую ваше приложение обрабатывает в данный момент времени.
Что является более производительным, зависит от того, как ваше приложение использует эти объекты, объем данных, которые оно обрабатывает, и даже объем памяти, доступной вашему процессу во время выполнения.
Не со стандартным списком TObjectList. Он (на самом деле базовый TList) написан для поддержки пользовательской функции сортировки с использованием CustomSort, но для пользовательского IndexOf такого положения нет. Конечно, вы можете написать свою собственную реализацию чего-то, что работает таким образом.