TList — Тип Delphi


Delphi TList записей

Мне нужно сохранить временный список записей и думал, что TList будет хорошим способом сделать это? Однако я не уверен, как это сделать с помощью TList и задавался вопросом, было ли это лучше всего, а также если у кого-нибудь есть примеры того, как это сделать?

Самый простой способ — создать собственный потомок TList . Здесь представлено быстрое приложение консоли для примера:

Это устраняет несколько вещей:

  • Он обрабатывает освобождение памяти.
  • Вам не нужно вводить все, чтобы использовать его.

Как сказал Реми и Уоррен, это немного больше, потому что вам нужно выделять память при добавлении новых записей.

Во-первых, если вы хотите совместить классический TList с записями, вам нужно будет:

  • Выделите свои записи в куче, а не в стеке. Используйте GetMem как Remy.
  • Возьмите адрес записи и добавьте ее в TList.
  • При удалении элемента из списка и его использовании разыщите его:
  • Не забудьте освободить и очистить, потом.

Сочетание списков с записями требует так много «указателей и кучи-управления», что такая техника будет только в пределах возможностей эксперта.

Альтернативы тому, что вы просили, чтобы все еще использовать что-то, называемое TList, включают использование TList стиля generics.collections с типами записей, которые будут иметь все преимущества TList, но потребуют от вас в основном сделать много целых копий для получения данных.

Самые идиоматические способы Delphi делать то, что вы просите, либо:

используйте TList или TObjectList с типами классов вместо записи. Обычно вы заканчиваете подклассификацию TList или TObjectList в этом случае.

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

Используйте generics.Collections TList с вашими классами. Это позволяет избежать подкласса TList или TObjectList каждый раз, когда вы хотите использовать список с другим классом.

Пример кода, показывающий динамические массивы:

Теперь для некоторой справочной информации о том, почему TList нелегко использовать с типами записей:

TList лучше подходит для использования с типами классов, потому что переменная типа «TMy как значение указателя, которое принадлежит TList.

Переменные типа Record — это значения-типы в Delphi, тогда как значения класса неявно являются значениями ссылки. Вы можете думать о значениях ссылки в качестве указателей стелса. Вам не нужно разыгрывать их, чтобы получить их содержимое, но когда вы добавляете его в TList, вы фактически добавляете указатель на TList, не создаете копию или не выделяете какую-либо новую память.

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

Вы можете ознакомиться с нашей оболочкой TDynArray. Он определен в модуле с открытым исходным кодом, работающем от Delphi 6 до XE.

С помощью TDynArray вы можете получить доступ к любому динамическому массиву (например, TIntegerDynArray = array of integer или TRecordDynArray = array of TMyRecord ) с помощью свойств и методов типа TList , например. Count, Add, Insert, Delete, Clear, IndexOf, Find, Sort и некоторые новые методы, такие как LoadFromStream, SaveToStream, LoadFrom и SaveTo , которые позволяют быстро выполнять двоичную сериализацию любого динамического массива, даже содержащие строки или записи — метод CreateOrderedIndex также доступен для создания отдельного индекса в соответствии с содержимым динамического массива. Вы также можете сериализовать содержимое массива в JSON, если хотите. Slice, Reverse или Copy также доступны.

Он будет обрабатывать динамический массив записей и даже записи в записях со строками или другими динамическими массивами внутри.

При использовании внешней переменной Count вы можете значительно ускорить добавление элементов в указанном динамическом массиве.

Существует также класс TDynArrayHashed , который позволяет внутреннее хеширование содержимого динамического массива. Он очень быстр и способен хэшировать любые данные (есть стандартные хешеры для строк, но вы можете предоставить свои собственные — даже хэш-функция может быть настроена).

Обратите внимание, что TDynArray и TDynArrayHashed являются только оболочками вокруг существующей переменной динамического массива. Поэтому вы можете инициализировать оболочку TDynArray при необходимости, чтобы более эффективно обращаться к любому родному динамическому массиву Delphi.

Класс TList

Основой класса TList является список указателей. Сам список представляет собой динамический массив указателей, к которому можно обратиться через индексированное свойство

property Items[Index: Integer]: Pointer;

Нумерация элементов начинается с нуля.

Прямой доступ к элементам массива возможен через свойство

TPointerList = array[0..MaxListSize-1] of Pointer;

property List: PPointerList;

которое имеет атрибут «только для чтения».

Так как элементы списка являются указателями на некоторые структуры, прямое обращение к составным частям этих структур через свойство items невозможно. Как это можно сделать, рассказывается ниже в примере.

В списке могут содержаться указатели на разнородные структуры. Не обязательно хранить в списке только указатели на объекты или указатели на записи.

Реализованные в классе TList операции со списком обеспечивают потребности разработчика и совпадают с операциями списка строк.

Для добавления в конец списка нового указателя используется метод

Прямое присваивание значения элементу, который еще не создан при помощи метода Add , вызовет ошибку времени выполнения.

Новый указатель можно добавить в нужное место списка. Для этого используется метод

procedure Insert(Index: Integer; Item: Pointer);

В параметре index указывается необходимый порядковый номер в списке.

Перенос существующего элемента на новое места осуществляется методом

procedure Move(Curlndex, Newlndex: Integer);


Параметр CurIndex определяет старое положение указателя. Параметр NewIndex задает новое его положение.

Также можно поменять местами два элемента, определяемые параметрами Indexl и Index2 :

procedure Exchange(Indexl, Index2: Integer);

Для удаления указателей из списка используются два метода. Если известен индекс, применяется метод

Если известен сам указатель, используется метод

function Remove(Item: Pointer): Integer;

Эти методы не уменьшают объем памяти, выделенной под список. При необходимости сделать это следует использовать свойство capacity . Также существует метод Expand , который увеличивает отведенную память автоматически в зависимости от текущего размера списка.

Для того чтобы метод сработал, необходимо, чтобы count = Capacity. Алгоритм работы метода представлен в табл. 7.1.

Таблица 7.1. Алгоритм увеличения памяти списка

Использование generics в Delphi: неизвестный тип данных TList

Пробую использовать generics:

но RAD Studio мне подчеркивает неизвестный тип данных TList , хотя в коде var List: TList; — никаких проблем нет. В чем тут может быть проблема? Юнит Classes , естественно, подключен.

1 ответ 1

Ответ найден: нужно подключить еще и юнит System.Generics.Collections

Всё ещё ищете ответ? Посмотрите другие вопросы с метками delphi generics delphi-xe2 или задайте свой вопрос.

Похожие

Подписаться на ленту

Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.

дизайн сайта / логотип © 2020 Stack Exchange Inc; пользовательское содержимое попадает под действие лицензии cc by-sa 4.0 с указанием ссылки на источник. rev 2020.11.11.35402

TList — Тип Delphi

Список для хранения указателей на любые объекты.

Используется как «интеллектуальная» альтернатива динамических массивов.
Он имеет методы добавления, удаления, поиска вхождения, расширения и пр. Быстр и надежен в использовании. TList это по праву «рабочая лошадка» программ на Delphi.
Изначально приспособлен для хранения указателей pointer или TObject, но легко может быть использован для хранения любых данных, занимающих в памяти размер не более размера pointer, т.е. не более 4 байт. Например boolean (1 байт), word(2 байта) и наиболее часто используется для integer (4 байта). Это не означает, что нельзя хранить записи и пр — просто для этого надо будет написать кое-что дополнительно.

Илон Маск рекомендует:  Атрибут coords в HTML

Вот пример для хранения в TList целых чисел — индексов строк из списка строк TStringList, у которых первая буква ‘Z’:

Применяем дженерики в RAD Studio Delphi. Создаем библиотеку сортировки списков однотипных объектов

Сегодня будем создавать в RAD Studio Delphi библиотеку классов, реализующих сортировку списков однотипных объектов.

Цель задачи

Прикладной разработчик должен получить инструмент для создания дочерних классов, в которых можно:

  • оперировать с объектами списка;
  • применять различные правила сравнения объектов;
  • применять различные алгоритмы сортировки объектов.

На выходе должна получиться библиотека классов, которая позволяет:

  • прикладному разработчику сортировать любой из 100 объектов любым из 100 методов сортировки;
  • дорабатывать и поддерживать новые алгоритмы или новые типы объектов в течении одного дня силами одного специалиста.

При создании необходимо учесть, что решение должно удовлетворять следующей модели:

  • Количество алгоритмов сортировки — 100;
  • Типы объектов доступных для сортировки — 100;
  • Количество разработчиков, одновременно работающих с библиотекой, для создания типов объектов и алгоритмов сортировки — 100.
  • Время разработки всех алгоритмов сортировки и типов объектов — 2 дня.

Приступаем

Сортировка по возрастанию — это перестановка элементов набора данных таким образом, чтобы в результате каждый последующий элемент набора был больше предыдущего. Сортировка по убыванию — то же самое, только обход результирующего набора данных нужно начинать с конца.

Сравнение объектов

Чтобы понять, какой элемент набора данных больше другого, для базовых типов необходимо применять оператор сравнения. А как быть с объектами? Базовый модуль System.Generics.Defaults включает в себя нужный нам интерфейс и реализацию класса

В интерфейсе видим единственный метод Compare вида

На входе два параметра типа объект, а на выходе целое число (0 — объекты равны, -1 — первый меньше второго, 1 — первый больше второго).

Для сравнения наших объектов будем использовать свои функции сравнения, которые описывают логику правил сравнения для каждого типа объектов. Заключим такие функции в отдельный класс TAllComparison. В нем и описываются все наши функции сравнения, они имеют тот же вид

А класс TComparer(T) как раз служит для сравнения двух объектов путем вызова метода Compare.
Можно использовать сравнение по умолчанию (Default), или создать свой метод сравнения Construct, чем мы и займемся.

Для удобства описание всех объектов будем хранить в отдельном модуле AllObjects. Здесь же будем хранить описание всех 100 созданных нами объектов.

Операции с объектами

Для осуществления операций с объектами списка в Delphi уже имеется нужный нам параметризированный класс, он же дженерик, с нужными нам методами

Вообще, универсальные параметризированные типы (дженерики) появились еще в Delphi 2009, но для нашего примера я использую RAD Studio Berlin 10.1 UPD1. Если у вас что-то не компилируется, нужно будет допилить пример для своей версии Delphi.


Пишем основной класс нашей библиотеки наследник TList(T)

Основной метод нашей задачи SortBy, опишем его использование далее.

Сортировка объектов

Пишем класс TAllSort, который содержит описание всех 100 методов сортировки вида:

На входе метод получает массив данных из списка, метод сравнения объектов, начальную позицию для сортировки, количество элементов в наборе. На выходе получаем количество произведенных в наборе данных перестановок.

Для удобства, все методы сортировки будем держать в отдельном модуле SortMethods.

Демонстрация

В качестве демонстрации решения представляю 2 механизма сортировки: «Быстрая» и «Пузырьком». Сортировать будем 2 типа объектов: список двумерных векторов (упорядоченные пары) типа Integer и список со строками.

Для начала отсортируем массив строк «пузырьками»:

А теперь «быстро» отсортируем массив двумерных векторов:

Вот такой результат получился с векторами:

Keeper’s blog

== I’m starting with the man in the mirror ==

Social Icons

Pages

пятница, 29 июля 2011 г.

Используем дженерики в Delphi! — Часть 1 (Введение)

[Содержание]
[Часть 1 — Введение в дженерики] [Часть 2 — Системные классы] [Часть 3 — Приложение]

  1. Что такое дженерики и зачем они нужны?
  2. Преимущества использования дженериков
    1. Безопасность типов
    2. Эффективность
    3. Максимальное повторное использование кода
  3. Встроенные обощенные классы в Delphi
  4. Что «поддается обобщению» в Delphi?
    1. Обобщенные методы
    2. Обобщенные классы
    3. Обобщенные записи
  5. Заключение

1. Что такое дженерики и зачем они нужны?
Наличие обобщений в языке позволяет создавать открытые типы, которые превращаются в закрытые на этапе компиляции. Синтаксис дженериков на примере обобщенной записи TPoint приведен в Листинге 1:
Листинг 1 — Объявление обобщенной записи TPoint
Сразу бросаются в глаза отличия от декларирования обычной записи — наличие в имени записи и кооринат X и Y этого же типа T . T здесь — неуточненный тип, который будет указан позже, при создании конкретного экземпляра записи.
Предположим, что мы решили использовать в приложении «дробные» точки (например, Double ). Все, что нужно сделать — объявить следующий закрытый тип:
Листинг 2 — Использование обобщенной записи TPoint в качестве «дробной» точки
А если нам понадобится целый тип, мы просто изменим Double на Integer :
Листинг 3 — Использование обобщенной записи TPoint в качестве «целой» точки
Просто, не правда ли? MyPoint: TPoint и MyPoint: TPoint — уже являются закрытыми типами и подчиняются все правилам, справедливым для обычных, необобщенных типов.

Может возникнуть вопрос: могу ли я сделать это без дженериков? Конечно, можете. Правда, лишитесь ряда преимуществ.

2. Преимущества использования дженериков
2.1. Безопасность типов
Когда необходимо повысить безопасность типов и избежать ошибок их несоответствия во время выполнения приложения — дженерики могут прийти на помощь. Для демонстрации сравним стандартный класс TList и его обобщенный «аналог» TList . Как известно, TList хранит массив указателей на объекты, причем тип этих объектов может быть различен. Рассмотрим следующий пример:
Листинг 4 — Вызов метода класса TCustomer для элементов TList
Теперь представьте, что передаваемый TList содержит не только экземпляры TCustomer . Для PrintCustomersInfo это будет катострофично и приведет к Invalid Type Cast , в процедуре PrintCustomersInfo2 мы избежали этого путем дополнительных проверок.

Но разве не замечательно бы было отдать такие проверки на откуп компилятору при сборке приложения? Дженерики позволяют это сделать:
Листинг 5 — Вывод информации о клиентах через TList
Заметили, что код уменьшился и стал более читаемым? Кроме того, за тем, чтобы в TList не попало ничего лишнего уже проследил компилятор.
2.2. Эффективность
Дополнительная эффективность при использовании дженериков — возможно, одно из главных их преимуществ. Обобщения предоставляют компилятору больше информации, не исключая данные о типе во время исполнения приложения. Такой код проще писать, эффективнее заниматься отладкой приложения. Кроме того, в рассматриваемом примере ассемблерный код с дженериками ( PrintCustomersInfo3 ) содержит до 10 инструкций меньше (по сравнению с PrintCustomersInfo2 ).
2.3. Максимальное повторное использование кода
Обобщенный класс, код для которого был написан всего 1 раз, может использоваться многократно. Так, без переписывания кода, TList может быть использован для создания списка целых чисел ( TList ), строк ( TList ) и т.д.

В любом случае, эти преимущества достаточно существенны для того, чтобы пользоваться ими в полной мере.

3. Встроенные обощенные классы в Delphi
«Из коробки» в Delphi уже имеется ряд стандартных обобщенных классов, которые можно использовать при написании приложений. Находятся они в модулях Generics.Defaults и Generics.Collections . Основные классы и типы данных приведены в Таблицах 1 и 2.
Таблица 1 — Некоторые классы модуля Generics.Defaults

IComparer Обобщенный интерфейс IComparer предназначен для сравнения двух значений одинакового типа
IEqualityComparer Обобщенный интерфейс IEqualityComparer используется для проверки равенства двух значений
TComparer Базовый обощенный класс для классов, реализующих интерфейс IComparer
TEqualityComparer Базовый обощенный класс для классов, реализующих интерфейс IEqualityComparer
TCustomComparer Базовый обощенный класс для классов, реализующих интерфейсы IComparer и IEqualityComparer

Таблица 2 — Некоторые классы и типы модуля Generics.Collections

Классы
TArray Класс, содержащий статические методы для поиска и сортировки обобщенного массива
TDictionary ,
TObjectDictionary
Словарь (коллекция пар ключ-значение)
TList ,
TObjectList
Упорядоченный список
TStack ,
TObjectStack
Реализация стека (последний пришел, первый вышел)
TQueue ,
TObjectQueue
Реализация очереди (первый пришел, первый вышел)
Типы
TPair Запись, хранящая пару ключ-значение
Примечание: как и аналоги из модуля Classes , обощенные «объектные» классы относительно «необъектных» (например, TObjectList по сравнению с TList ) позволяют хранить объекты в качестве своих элементов, а также автоматически следить за их жизненным циклом

Использовать стандартные обобщенные классы довольно просто: включаем соответствующие модули в раздел uses и задействуем нужные нам классы. В Листинге 6 приведен пример работы со списком целых чисел на основе обобщенного класса TList .
Листинг 6 — Пример использования TList для создания списка целых чисел
Более подробно системные классы будут рассмотрены во 2-м разделе.

Илон Маск рекомендует:  Тег h2

4. Что «поддается обобщению» в Delphi?
Естественно, что в Delphi имеется возможность не только использовать имеющуюся библиотеку дженериков, но и создавать свои собственные. Обобщенными могут быть классы, интерфейсы и записи. Также поддерживается создание обобщенных методов (процедур и функций).
4.1. Обобщенные методы
Самым простым примером обобщенного метода может служить процедура для обмена значений переменных:
Листинг 7 — Пример дженериковой процедуры Swap
Использовать такую процедуру можно следующим образом:
Листинг 8 — Использование дженериковой процедуры Swap
Результат приведен на Рисунке 1:

Рисунок 1 — Пример использования Swap

4.2. Обобщенные классы
Приведем пример обобщенного класса массива:
Листинг 9 — Пример обобщенного класса массива TGenericArray
Посмотрим на вариант его использования:
Листинг 10 — Использование обобщенного класса массива TGenericArray
Результат приведен на Рисунке 2.

Рисунок 2 — Пример использования TGenericArray
4.3. Обобщенные записи
Пример обобщенной записи TPoint уже был приведен в начале раздела. Гляньте на нее еще разок.

TList — Тип Delphi

Класс TList — универсальный список. Он представляет собой массив нетипированных указателей и поэтому годится для хранения набора любых, в том числе разнотипных, данных и объектов. При добавлении/удалении в список данные не создаются и не уничтожаются — эта обязанность лежит на программисте.

Приведем доступные ему методы и свойства класса:

property Items[Index: Integer]: Pointer; Возвращает указатель на содержимое элемента списка с индексом Index. Это свойство является векторным свойством, принимаемым по умолчанию, и его имя можно при записи опускать.

property Count: Integer; Определяет число элементов в списке.

property Capacity: Integer; Определяет максимальное число элементов в списке. Оно может изменяться как явно — пользователем, так и при добавлении элементов в список, в том случае, когда Count>=Capacity. Максимальная емкость списка — 16380 элементов.

Управляют списком следующие методы:

function Add(Item: Pointer): Integer; Добавляет в конец списка элемент, который будет равен Item (т. е. указывать на те же данные).

function Remove(Item: Pointer): Integer; Удаляет из списка элемент, который равен Item.

procedure Insert(Index: Integer; Item: Pointer) ; Вставляет элемент, равный Item, перед элементом с индексом Index.

procedure Delete(Index: Integer); Удаляет из списка элемент с индексом Index.


procedure Clear; Очищает список, устанавливая величины Count и Capacity в 0.

procedure Exchange(Indexl, Index2: Integer); Меняет местами элементы списка с индексами Indexl и Index2.

function Expand: TList; При соблюдении равенства Count=Capacity расширяет список. При емкости списка менее пяти элементов, он по умолчанию расширяется на четыре элемента, при пяти-восьми — на восемь, более восьми — на шестнадцать.

function First: Pointer; function Last: Pointer; Возвращают значения первого п последнего (с индексом Count-1) элементов списка соответственно.

function IndexOf(Item: Pointer): Integer; Возвращает индекс элемента, равного Item.

procedure Move(CurIndex, Newlndex: Integer) ; Перемещает элемент списка с положения Curlndex в положение Newlndex.

procedure Pack; Упаковывает список, сдвигая элементы к началу на пустующие места.

Наконец, если приведенных методов почему-либо недостаточно, то свойство

property List: pPointerList;

TPointerList = array[0..65520 div SizeOf(Pointer)] of Pointer;

возвращает указатель непосредственно на список указателен ((ко) означает, что свойство доступно только для чтения).

Класс TList, массив указателей

Класс TList, массив указателей

С самой первой версии в Delphi существовал еще один стандартный массив -класс TList. В отличие от всех ранее нами рассмотренных массивов, TList представляет собой массив указателей.

Краткий обзор класса TList

Класс TList хранит указатели в формате массива. Указатели могут быть любыми. Они могут указывать на записи, строки или объекты. Класс имеет специальные методы для вставки и удаления элементов, поиска элемента в списке, перестановки элементов и, в последних версиях компилятора, для сортировки элементов в списке. Как и любой другой массив, TList может использовать операцию [ ]. Поскольку свойство Items является свойством по умолчанию, то для получения доступа к указателю с индексом i вместо MyList.Item[i] можно записывать MyList[i]. Индексация в классе TList всегда начинается с 0.

Несмотря на высокую гибкость класса TList, иногда при его использовании возникают проблемы.

Одна из проблем встречается очень часто: при уничтожении экземпляра TList память, выделенная под оставшиеся в нем элементы, не освобождается. В некотором роде это даже преимущество, поскольку можно быть уверенным, что TList никогда не освободит память, используемую его элементами. Один и тот же элемент можно поместить одновременно в несколько списков, не боясь, что он будет удален по ошибке. К сожалению, многие программисты склонны считать, что TList работает точно так же, как любой компонент формы (т.е. при уничтожении формы уничтожаются и все ее компоненты). Но в отношении TList это не так, поэтому необходимо отдельно позаботиться о том, чтобы при уничтожении списка удалялись и все его элементы.

Однако существует еще одна трудноуловимая ошибка, которую очень часто совершают при написании кода удаления всех элементов из списка. Во многих случаях код удаления выглядит следующим образом:

for i := 0 to pred(MyList.Count) do begin

if SomeConditionApplies(i) then begin

где ScmeConditionApplies — некоторая произвольная функция, которая определяет, удалять или нет элемент с индексом i.

Все мы привыкли к тому, что значение переменной цикла должно увеличиваться. Именно в этом-то и заключается ошибка. Предположим, что в массиве находится три элемента. В таком случае код в цикле будет выполнен три раза: для индексов 0, 1 и 2. Пусть при первом выполнении цикла условие выполняется. При этом освобождается объект с индексом 0, а затем элемент с индексом 0 удаляется из списка. После первого выполнения цикла в списке остается два элемента, но их индексы теперь 0 и 1, а не 1 и 2. При втором выполнении цикла, при соблюдении условия, освобождается объект с индексом 1 (который, если вы помните, был изначально элементом с индексом 2), после чего удаляется элемент с индексом 1. После этого в списке остается всего один элемент. И его индекс 0. При третьем выполнении цикла код пытается освободить память, ранее выделенную под объект, индекс которого 2, и в результате генерируется исключение «list index out of bounds».

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

Для освобождения всех элементов списка используется следующий код, а не вызов метода Delete для каждого элемента:

for i := 0 to pred(MyList.Count) do

Еще одной проблемой при использовании класса TList является создание производного класса. Если попытаться это сделать, можно столкнуться с разного рода проблемами, вызванными тем, что методы TList являются статическими, к тому же имеют приватные поля, которые не доступны, и т.д. Можно только посоветовать не пытаться порождать новые классы от TList.TList — это не тот класс, на основе которого можно создавать производные классы. Он был создан не таким расширяемым, как, например, TString. При необходимости можно создать отдельный класс, который для хранения данных использует класс TList. Применяйте в данном случае делегирование, а не наследование.

При первом написании предыдущего параграфа автор книги не знал, что компания Borland сделала с классом TList в версии Delphi 5. В Delphi 5 по каким-то непостижимым причинам было изменено функционирование класса TList с целью обеспечения поддержки нового производного класса — TObjectList.TObjectList предназначен для хранения экземпляров объектов. Он находится в модуле Contnrs, о котором мы поговорим чуть позже.

Что же изменилось? В версиях до Delphi 5 TList очищался путем освобождения внутреннего массива указателей, что было операцией класса O(1). Поскольку компания Borland хотела, чтобы класс TObjectList при определенных условиях мог освобождать содержащиеся в нем объекты, она для обеспечения такой функциональности изменила основной принцип работы TList. В Delphi, начиная с версии 5, и, конечно же, Kylix, класс TList очищается путем вызова для каждого элемента нового виртуального метода Notify. Метод TList.Notify не выполняет никаких операций, но метод TObjectList.Notify при удалении элементов из списка освобождает занимаемую ими память.

Вы можете спросить: «Ну и что?» Дело в том, что этот новый метод очистки содержимого класса TList принадлежит к операциям класса О(n). Таким образом, чем больше элементов в списке, тем больше времени потребуется на его очистку. По сравнению с предыдущими версиями TList, новая версия стала работать гораздо медленнее. Каждый экземпляр каждого класса, использующего TList, теперь будет работать медленнее. И помните, единственной причиной снижения быстродействия стало нежелание компании Borland воспользоваться делегированием, вместо наследования. По мнению компании, было намного удобнее изменить стандартный класс.

Илон Маск рекомендует:  Что такое код msession_set_array

И что еще хуже с точки зрения объектно-ориентированного программирования, мы получили ситуацию, когда для поддержки производного класса был изменен родительский класс. TList не должен освобождать свои элементы — это стало правилом еще с версии Delphi1. Тем не менее, он был изменен для того, чтобы такая возможность поддерживалась его дочерними классами (а фактически только одним классом из VCL Delphi 5 — классом TObjectList).

Денни Торп (Denny Thorpe), один из самых толковых разработчиков в отделе научных исследований компании Borland, в своей книге «Разработка компонент Delphi» (Delphi Component Design) [23] сказал следующее:

«TList — это рабочая лошадка, а не порождающий класс. При необходимости использования списка создайте простой интерфейсный класс (порожденный от TObject, а не от TList), который будет иметь только нужные вам эквиваленты свойств и функций TList, и возвращаемые типы значений функций и параметров методов которого соответствуют типу хранящихся в списке данных. В таком случае интерфейсный класс будет содержать в себе класс TList и использовать его для хранения данных. Реализуйте функции и методы доступа к свойствам этого интерфейсного класса в виде однострочных вызовов соответствующих методов или свойств внутреннего класса TList с применением соответствующих преобразований типов».

Очень жаль, что книга Денни Торпа не внесена в перечень книг, обязательных для прочтения в отделе научных исследований компании Borland.

Справочник по компонентам Delphi. Часть 1
Страница 2. Класс TList

Класс TList

Класс TList — универсальный список. Он представляет собой массив нетипированных указателей и поэтому годится для хранения набора любых, в том числе разнотипных, данных и объектов. При добавлении/удалении в список данные не создаются и не уничтожаются — эта обязанность лежит на программисте. Приведем доступные ему методы и свойства класса:

property Items[Index: Integer]: Pointer;

Возвращает указатель на содержимое элемента списка с индексом Index. Это свойство является векторным свойством, принимаемым по умолчанию, и его имя можно при записи опускать (см. раздел «Свойства»).

Определяет число элементов в списке.

Определяет максимальное число элементов в списке. Оно может изменяться как явно — пользователем, так и при добавлении элементов в список, в том случае, когда Count>=Capacity. Максимальная емкость списка — 16380 элементов.

Управляют списком следующие методы:

function Add(Item: Pointer): Integer;

Добавляет в конец списка элемент, который будет равен Item (т. е. указывать на те же данные).

function Remove(Item: Pointer): Integer;


Удаляет из списка элемент, который равен Item.

procedure Insert(Index: Integer; Item: Pointer) ;

Вставляет элемент, равный Item, перед элементом с индексом Index.

procedure Delete(Index: Integer);

Удаляет из списка элемент с индексом Index.

Очищает список, устанавливая величины Count и Capacity в 0.

procedure Exchange(Indexl, Index2: Integer);

Меняет местами элементы списка с индексами Indexl и Index2.

При соблюдении равенства Count=Capacity расширяет список. При емкости списка менее пяти элементов, он по умолчанию расширяется на четыре элемента, при пяти-восьми — на восемь, более восьми — на шестнадцать.

function First: Pointer; function Last: Pointer;

Возвращают значения первого п последнего (с индек­сом Count-1) элементов списка соответственно.

function IndexOf(Item: Pointer): Integer;

Возвращает индекс элемента, равного Item.

procedure Move(CurIndex, Newlndex: Integer) ;

Перемещает элемент списка с положения Curlndex в положение Newlndex.

Упаковывает список, сдвигая элементы к началу на пустующие места.

Наконец, если приведенных методов почему-либо недостаточно, то свойство

возвращает указатель непосредственно на список указателен ((ко) означает, что свойство доступно только для чтения).

Delphi TList записей

Мне нужно сохранить временный список записей и думал, что TList будет хорошим способом сделать это? Однако я не уверен, как это сделать с помощью TList и задавался вопросом, было ли это лучше всего, а также если у кого-нибудь есть примеры того, как это сделать?

Самый простой способ — создать собственный потомок TList . Здесь представлено быстрое приложение консоли для примера:

Это устраняет несколько вещей:

  • Он обрабатывает освобождение памяти.
  • Вам не нужно вводить все, чтобы использовать его.

Как сказал Реми и Уоррен, это немного больше, потому что вам нужно выделять память при добавлении новых записей.

Во-первых, если вы хотите совместить классический TList с записями, вам нужно будет:

  • Выделите свои записи в куче, а не в стеке. Используйте GetMem как Remy.
  • Возьмите адрес записи и добавьте ее в TList.
  • При удалении элемента из списка и его использовании разыщите его:
  • Не забудьте освободить и очистить, потом.

Сочетание списков с записями требует так много «указателей и кучи-управления», что такая техника будет только в пределах возможностей эксперта.

Альтернативы тому, что вы просили, чтобы все еще использовать что-то, называемое TList, включают использование TList стиля generics.collections с типами записей, которые будут иметь все преимущества TList, но потребуют от вас в основном сделать много целых копий для получения данных.

Самые идиоматические способы Delphi делать то, что вы просите, либо:

используйте TList или TObjectList с типами классов вместо записи. Обычно вы заканчиваете подклассификацию TList или TObjectList в этом случае.

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

Используйте generics.Collections TList с вашими классами. Это позволяет избежать подкласса TList или TObjectList каждый раз, когда вы хотите использовать список с другим классом.

Пример кода, показывающий динамические массивы:

Теперь для некоторой справочной информации о том, почему TList нелегко использовать с типами записей:

TList лучше подходит для использования с типами классов, потому что переменная типа «TMy как значение указателя, которое принадлежит TList.

Переменные типа Record — это значения-типы в Delphi, тогда как значения класса неявно являются значениями ссылки. Вы можете думать о значениях ссылки в качестве указателей стелса. Вам не нужно разыгрывать их, чтобы получить их содержимое, но когда вы добавляете его в TList, вы фактически добавляете указатель на TList, не создаете копию или не выделяете какую-либо новую память.

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

Вы можете ознакомиться с нашей оболочкой TDynArray. Он определен в модуле с открытым исходным кодом, работающем от Delphi 6 до XE.

С помощью TDynArray вы можете получить доступ к любому динамическому массиву (например, TIntegerDynArray = array of integer или TRecordDynArray = array of TMyRecord ) с помощью свойств и методов типа TList , например. Count, Add, Insert, Delete, Clear, IndexOf, Find, Sort и некоторые новые методы, такие как LoadFromStream, SaveToStream, LoadFrom и SaveTo , которые позволяют быстро выполнять двоичную сериализацию любого динамического массива, даже содержащие строки или записи — метод CreateOrderedIndex также доступен для создания отдельного индекса в соответствии с содержимым динамического массива. Вы также можете сериализовать содержимое массива в JSON, если хотите. Slice, Reverse или Copy также доступны.

Он будет обрабатывать динамический массив записей и даже записи в записях со строками или другими динамическими массивами внутри.

При использовании внешней переменной Count вы можете значительно ускорить добавление элементов в указанном динамическом массиве.

Существует также класс TDynArrayHashed , который позволяет внутреннее хеширование содержимого динамического массива. Он очень быстр и способен хэшировать любые данные (есть стандартные хешеры для строк, но вы можете предоставить свои собственные — даже хэш-функция может быть настроена).

Обратите внимание, что TDynArray и TDynArrayHashed являются только оболочками вокруг существующей переменной динамического массива. Поэтому вы можете инициализировать оболочку TDynArray при необходимости, чтобы более эффективно обращаться к любому родному динамическому массиву Delphi.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL