Могу ли я передать одну функцию для 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 такого положения нет. Конечно, вы можете написать свою собственную реализацию чего-то, что работает таким образом.

Другие вопросы по тегам