Индексы для коллекции
В коллекции TSerializableCollection элементы упорядочены по возрастанию значения уникального идентификатора элемента. Такой порядок не всегда удобен. Например, конечному пользователю обычно нужен список, отсортированный по именам или по датам и т.п. С этой целью при создании коллекции в конструктор класса TSerializableCollection передается массив, содержащий так называемые индексы – объекты, задающие альтернативный порядок сортировки элементов коллекции. Индексы должны быть созданы заранее, до вызова конструктора коллекции. Все индексы представляются экземплярами классов, производных от TDataIndex. Конкретный класс выбирается в зависимости от типа признака, по которому сортируются данные. Если этот признак – строка (например, наименование объекта), создается индекс типа TStringIndex; если признак – дата, используется класс TDateTimeIndex и т.д. Есть еще специальный класс TCompoundIndex, предназначенный для сортировки элементов коллекции сразу по нескольким признакам.
Индексы предназначены не только для задания порядка элементов коллекции. Основной причиной использования индексов является необходимость быстрого поиска элементов по значению индексируемого признака. Индексы позволяют не только быстро находить отдельные элементы, но также выделять группы элементов, для которых значение признака лежит в определенном интервале. Кроме того, индексы могут быть уникальными, т.е. не допускающими дублирования значения признака. При добавлении и изменении элементов коллекция опрашивает все свои уникальные индексы на предмет того, не вызовет ли каждое конкретное изменение нарушения уникальности какого-либо индекса. Если такая ситуация имеет место, предполагаемые изменения отклоняются.
Изначально все индексы находятся в неактивном состоянии, т.е. не занимают память и не обновляются при каждом изменении коллекции. При попытке воспользоваться индексом, например, для поиска элемента, индекс автоматически активизируется. Его состоянием можно управлять с помощью свойства Active, объявленного в классе TDataIndex.
Следует обратить внимание на то, что в часто изменяемых коллекциях лучше постоянно держать все уникальные индексы в активном состоянии. В противном случае, при любом изменении данных выполняется медленный последовательный перебор элементов коллекции индексом для выяснения того, что значение индексируемого признака измененного элемента уникально в пределах набора данных. Каждый активный индекс содержит полный набор элементов коллекции, отсортированный по значению соответствующего признака. Свойство Descending индекса по умолчанию равно False. Это означает, что элементы сортируются по возрастанию значения признака. Если свойство Descending равно True, элементы сортируются по убыванию значения признака. Для класса TStringIndex, если свойство CaseSensitive равно False, при сортировке и поиске элементов с помощью индекса регистр символов не принимается во внимание. Если это свойство равно True, регистр символов учитывается. К элементам сортированного списка можно обратиться через свойство ItemList индекса, которое возвращает массив указателей на элементы коллекции – экземпляры класса TSerializableObject. Число элементов в этом массиве определяется свойством Count основной коллекции, к которой можно обратиться через свойство Owner индекса. Свойство Unique определяет, является ли данный индекс уникальным. Значение этого свойства передается в конструктор класса индекса и в дальнейшем не может быть изменено. Кроме того, при создании индекса в его конструктор обычно передается адрес функции, которая возвращает значение индексируемого признака для элемента коллекции. В случае класса TCompoundIndex вместо этого передается адрес функции, которая сравнивает между собой два элемента коллекции. В каждом индексе есть методы для поиска элементов. Метод ScanPointer выполняет линейный поиск указателя во внутреннем массиве индекса. Этим методом стоит пользоваться только если никакие другие не подходят. Метод Contains возвращает True, если в коллекции присутствует элемент с указанным значением признака.
Функция IndexOf находит во внутреннем массиве первый элемент с определенным значением признака и возвращает его индекс в качестве результата, а функция SearchObject в аналогичной ситуации возвращает сам элемент. Во всех индексах, кроме TCompoundIndex, есть функции SelectRange для нахождения диапазона во внутреннем массиве, в котором значение признака больше или равно значению Key1 и меньше значения Key2. При сортировке элементов по убыванию эти функции выделяют диапазон, в котором значение признака меньше или равно значению Key1 и больше значения Key2. Имеется также вариант этой функции, которая возвращает индекс первого элемента массива, для которого индексируемый признак больше или равен (меньше или равен в случае, когда Descending равно True) указанному значению. В классе TStringIndex есть еще метод StartsWith для выделения диапазона во внутреннем массиве, где значение признака для всех элементов начинается с указанной подстроки с учетом или без учета регистра символов в зависимости от значения свойства CaseSensitive.