Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Сортировка списка записей TList по произвольному полю записи с использованием указателей.

Delphi , Компоненты и Классы , Списки

 

В Delphi часто возникает задача сортировки списка записей TList<T> по различным полям записи. В данной статье мы рассмотрим несколько подходов к решению этой задачи, включая использование указателей, компараторов и альтернативные методы.

Постановка задачи:

Имеется список записей типа dmR00, хранящийся в TList<dmR00>. Необходимо реализовать функцию сортировки, которая позволяла бы сортировать список по любому из полей записи (например, ID, x, y, z) без жесткой привязки к конкретному полю в коде сортировки.

type
  dmR00 = record
    ID : integer;
    x, y, z : integer;
  end;

dmA00 = TList<dmR00>;

Решение 1: Использование компаратора (рекомендуемый подход)

Наиболее гибким и современным способом решения этой задачи является использование компараторов. Delphi предоставляет интерфейс IComparer<T> и класс TComparer<T>, которые позволяют создавать пользовательские функции сравнения для сортировки.

type
  TMyRecord = record
    Foo: integer;
    Bar: string;
  end;

  TMyList = TList<TMyRecord>;

  TMyField = (mfFoo, mfBar);

procedure SortList(List: TMyList; Field: TMyField);
var
  Comparer: IComparer<TMyRecord>;
begin
  case Field of
    mfFoo:
      Comparer := TComparer<TMyRecord>.Construct(
        function(const A, B: TMyRecord): integer
        begin
          Result := (A.Foo - B.Foo);
        end);

    mfBar:
      Comparer := TComparer<TMyRecord>.Construct(
        function(const A, B: TMyRecord): integer
        begin
          Result := CompareText(A.Bar, B.Bar);
        end);

  else
    exit;
  end;

  List.Sort(Comparer);
end;

// Пример использования:
var
  MyList := TList<TMyRecord>.Create;
begin
  // ... Заполнение списка ...

  SortList(MyList, mfFoo); // Сортировка по полю Foo
  SortList(MyList, mfBar); // Сортировка по полю Bar
end;

Преимущества:

  • Гибкость: Легко добавлять новые поля для сортировки, просто добавляя новые ветки в case оператор.
  • Типобезопасность: Компилятор проверяет типы данных, что снижает вероятность ошибок.
  • Чистый код: Код сортировки становится более читаемым и понятным.
  • Использование стандартных средств: Нет необходимости изобретать велосипед.

Решение 2: Использование указателей (менее предпочтительный, но полезный для понимания)

Хотя использование компараторов является предпочтительным, понимание работы с указателями может быть полезным.

type
  TMyRecord = record
    Foo: integer;
    Bar: string;
  end;

  PMyRecord = ^TMyRecord;

var
  MyList := TList<TMyRecord>.Create;

// Получение указателя на элемент списка:
var
  Items := MyList.List; // Получаем доступ к внутреннему динамическому массиву
  SomeItem: PMyRecord := @Items[0]; // Получаем указатель на первый элемент

// Работа с элементом по указателю:
SomeItem^.Foo := 42;
SomeItem.Bar := 'Hello world';

Важно! Указатели, полученные таким образом, действительны только до тех пор, пока размер списка не изменяется (добавление или удаление элементов может привести к перераспределению памяти, и указатели станут недействительными).

Использование указателей для сортировки (не рекомендуется):

Использовать указатели непосредственно в алгоритме сортировки крайне не рекомендуется из-за сложности и потенциальных проблем с управлением памятью. Лучше использовать компараторы.

Решение 3: Использование параметра Key (альтернативный подход, но не самый гибкий)

Предложенный автором исходного вопроса вариант с использованием параметра key и case оператора также возможен, но менее гибок и масштабируем, чем использование компараторов.

type
  dmR00 = record
    ID : integer;//key-0
    x,//key-1
    y,//key-2
    z : integer;//key-3
  end;

dmA00 = TList<dmR00>;

function Foo.BubbleSort(AList: dmA00; key:byte; out Error: String): Boolean;
var
  jRec, j1Rec: dmR00;
  i,j : integer;
begin
  try
    for i := 0 to AList.Count - 2 do
    begin
      for j := 0 to AList.Count - 2 - i do
      begin
        jRec := aList[j];
        j1Rec := aList[j+1];

        case key of
          0: if jRec.ID > j1Rec.ID then Swap(AList[j], AList[j+1]);
          1: if jRec.x > j1Rec.x then Swap(AList[j], AList[j+1]);
          2: if jRec.y > j1Rec.y then Swap(AList[j], AList[j+1]);
          3: if jRec.z > j1Rec.z then Swap(AList[j], AList[j+1]);
        end;
     end;
    end;
    Result := True;
  except
    on E: Exception do
    begin
      Error := ('Exception class name = ' + E.ClassName + sLineBreak +
        'Exception message = ' + E.Message);
      Result := False;
    end;
  end;
end;

Недостатки:

  • Ограниченная масштабируемость: При добавлении новых полей необходимо добавлять новые ветки в case оператор.
  • Менее читаемый код: case оператор может стать громоздким при большом количестве полей.
  • Отсутствие типобезопасности: Компилятор не проверяет, что значение key соответствует существующему полю записи.

Альтернативное решение: Индексированный список (для больших объемов данных)

Если работа ведется с очень большими списками записей и важна производительность, можно рассмотреть вариант с созданием отдельного индексированного списка. В этом случае, данные хранятся в одном списке (например, TList<dmR00>), а индексы (например, TList<Integer>) хранятся в другом списке. Сортировка выполняется над списком индексов, а доступ к данным осуществляется через отсортированный список индексов. Этот метод позволяет избежать копирования больших объемов данных при сортировке.

Заключение:

Для сортировки списка записей TList<T> по произвольному полю рекомендуется использовать компараторы. Этот подход обеспечивает гибкость, типобезопасность и чистый код. Использование указателей для непосредственной сортировки не рекомендуется из-за сложности и потенциальных проблем с управлением памятью. Альтернативные решения, такие как использование параметра key или индексированного списка, могут быть полезны в определенных ситуациях, но имеют свои ограничения. Выбор оптимального решения зависит от конкретных требований задачи и приоритетов (гибкость, производительность, простота кода).

Создано по материалам из источника по ссылке.

В статье рассматриваются различные подходы к сортировке списка записей в Delphi по произвольному полю, включая использование компараторов, указателей и параметра key, с рекомендацией использовать компараторы для гибкости и типобезопасности.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Списки ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-05-01 10:14:33/0.0040791034698486/0