При оптимизации ресурсоемких алгоритмов во встроенном языке 1С разработчики часто сталкиваются с выбором оптимальной структуры данных для организации быстрого кэша или поиска по ключу. Традиционно основными кандидатами выступают объекты Соответствие и ТаблицаЗначений с установленным индексом. Разберем теоретическую и практическую сложность работы с этими коллекциями, выясним их внутреннее устройство и определим, какую структуру эффективнее применять в тех или иных сценариях.
Для понимания разницы в скорости сопоставим теоретическую сложность (Big O) основных операций поиска по ключу:
Соответствие: Внутренне устроен на базе хеш-таблицы. Для извлечения значения по ключу платформа вычисляет хеш переданного объекта и обращается напрямую к соответствующей корзине (bucket). В среднем временная сложность поиска составляет O(1) — операция выполняется за условно константное время независимо от общего объема коллекции. В худшем случае (при высокой плотности коллизий, когда разные ключи попадают в одну корзину) сложность может стремиться к O(N), однако при штатном распределении ключей вероятность подобного сценария минимальна.ТаблицаЗначений: Индекс платформы 1С для колонок таблицы значений строится на основе древовидных структур данных (деревьев поиска). Поиск через НайтиСтроки() по проиндексированным колонкам выполняется за логарифмическое время — O(log N).СписокЗначений требует полного последовательного перебора элементов, что дает линейную сложность O(N).Таким образом, с точки зрения чистой алгоритмической сложности точечный поиск одиночного значения по ключу в объекте Соответствие быстрее, особенно по мере увеличения количества элементов в выборке.
С точки зрения лаконичности кода обращение к соответствию выглядит компактнее вызова поиска по строкам таблицы значений. Однако здесь кроется частая алгоритмическая ловушка. Рассмотрим типичный пример обращения к кэшу:
// Простое получение значения из кэша
ЗначениеКэша = Кэш[ИскомыйКлюч];
Если ЗначениеКэша = Неопределено Тогда
// Внимание: логика может сработать некорректно, если в кэше хранится само Неопределено!
КонецЕсли;
Проанализируем эту ситуацию: если коллекция Соответствие содержит ключ, значением которого явно является Неопределено, проверка через прямое чтение индексатора не позволит отличить реальное отсутствие ключа от присутствия ключа со значением по умолчанию. У Соответствия нет встроенного метода быстрой проверки наличия ключа, и гарантированно определить его существование можно только полным перебором. Если требуется строгая проверка факта присутствия строкового идентификатора, альтернативой может выступать объект Структура с методом Свойство():
ЗначениеКэша = Неопределено;
Если СтруктураКэша.Свойство(ИмяКлюча, ЗначениеКэша) Тогда
// Ключ гарантированно присутствует в коллекции
КонецЕсли;
При использовании таблицы значений результат поиска через НайтиСтроки() всегда детерминирован: метод возвращает пустой массив, если ни одной подходящей строки не найдено.
Отбор = Новый Структура("Артикул", ИскомыйАртикул);
НайденныеСтроки = ТаблицаЦен.НайтиСтроки(Отбор);
Если НайденныеСтроки.Количество() > 0 Тогда
СтрокаДанных = НайденныеСтроки[0];
КонецЕсли;
Важнейшее практическое различие между коллекциями заключается в способе сравнения ключей строкового типа:
Соответствии: Строковые ключи строго чувствительны к регистру символов. Ключи "Товар" и "товар" для соответствия являются двумя абсолютно разными элементами. Это обеспечивает максимальное быстродействие при хешировании, но требует предварительного приведения строк к единому регистру (например, через ВРег()), если поиск должен быть регистронезависимым.ТаблицеЗначений: Поиск строк в таблице значений выполняется по стандартным платформенным правилам сопоставления строк без учета регистра.Соответствия может выступать любой тип данных 1С (ссылки, числа, даты, булево). Для примитивных типов вычисление хеша происходит предельно быстро.Сравним накладные расходы при наполнении коллекций и потреблении оперативной памяти:
ТаблицаЗначений — тяжеловесный объект. Она хранит структуру колонок, строки таблицы, метаданные типов и сам индекс. Объект Соответствие расходует в разы меньше оперативной памяти при сопоставимом объеме хранимых пар ключ-значение.Индексы до заполнения строк, то каждый последующий вызов Добавить() или ЗаполнитьЗначения() вызовет моментальное перестроение дерева индекса. Это кардинально замедлит наполнение таблицы. При работе с таблицей значений всегда применяем правильный паттерн: сначала заполняем все данные, и только затем создаем индекс перед началом серии поисков:
// Правильный сценарий работы с индексами ТаблицыЗначений:
ТаблицаЦен = Новый ТаблицаЗначений;
ТаблицаЦен.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаЦен.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
// 1. Сначала наполняем таблицу данными
Для Каждого СтрДанных Из ИсточникДанных Цикл
НоваяСтрока = ТаблицаЦен.Добавить();
НоваяСтрока.Номенклатура = СтрДанных.Номенклатура;
НоваяСтрока.Цена = СтрДанных.Цена;
КонецЦикла;
// 2. Только после наполнения создаем индекс
ТаблицаЦен.Индексы.Добавить("Номенклатура");
Выбор структуры зависит от сложности критериев поиска:
Номенклатура, Характеристика и Склад), ТаблицаЗначений является штатным решением. Платформа поддерживает составные индексы (колонки перечисляются через запятую: Таблица.Индексы.Добавить("Номенклатура, Характеристика, Склад")). В Соответствии для этого пришлось бы искусственно конкатенировать значения в строковый ключ или использовать сложные структуры, что сводит на нет преимущество по скорости.ТаблицаЗначений позволяет хранить несколько строк с одинаковыми значениями ключевых колонок, и НайтиСтроки() вернет массив всех совпадений. Соответствие хранит строго одно уникальное значение на один ключ — повторная запись с тем же ключом перезаписывает старое значение.Разберем ограничения сред выполнения:
Соответствие доступен как на сервере, так и на клиенте (в тонком и веб-клиенте). Он может передаваться в качестве параметра между клиентом и сервером без дополнительных преобразований (при условии, что ключи и значения содержат сериализуемые типы данных).ТаблицаЗначений существует исключительно в серверном контексте (и на толстом клиенте). Передать таблицу значений на тонкий клиент напрямую невозможно — требуется конвертация в массив структур или временное хранилище.Подведем практический итог для выбора инструмента:
Соответствие, когда требуется максимально быстрый точечный поиск одиночного значения по одному уникальному ключу (кэширование ссылок, цен, реквизитов), важна экономия оперативной памяти или требуется передача данных между клиентом и сервером.ТаблицуЗначений, если необходим поиск по составному ключу из нескольких колонок, выборка может содержать несколько строк на один ключ, или данные в дальнейшем будут передаваться в запросы или табличные части.ИНДЕКСИРОВАТЬ ПО) и выполнением соединений средствами СУБД, минуя обработку коллекций в памяти сервера приложений средствами встроенного языка.