В практике разработки на платформе 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда данные, добавленные в коллекцию Соответствие, при обходе в цикле Для каждого возвращаются в совершенно непредсказуемом порядке. Особенно сбивает с толку момент, когда добавление одного нового элемента (например, начинающегося с нижнего подчеркивания) внезапно «перемешивает» все остальные записи, которые до этого момента казались стабильными. В этой статье мы подробно разберем внутреннюю логику платформы, выясним причины такого поведения и рассмотрим надежные способы реализации коллекций с сохранением порядка.
Разберем ситуацию, описанную в вопросе. Программист ожидает, что коллекция будет вести себя как список, сохраняя последовательность ввода. Однако Соответствие в 1С — это классическая реализация ассоциативного массива на базе хеш-таблицы (или сбалансированного дерева по хеш-кодам ключей). Рассмотрим основные причины отсутствия порядка:
Соответствие платформа вычисляет хеш-код ключа. На основе этого значения определяется местоположение элемента во внутренней структуре памяти (в так называемых «корзинах» или бакетах). Порядок обхода коллекции — это просто технический обход этих внутренних структур. Хеш-код строки «Банки» может быть больше хеш-кода строки «Склады», поэтому они встают не по алфавиту и не по времени добавления._СистемыНалогообложения изменило весь порядок. Хеш-таблицы имеют свойство динамически расширяться. Когда количество элементов достигает определенного порога (коэффициент заполнения), система выделяет новый блок памяти и перераспределяет все существующие элементы. В этот момент «визуальный» порядок в цикле Для каждого может измениться до неузнаваемости.Массив или ТаблицаЗначений, у Соответствия нет метода Получить(Индекс). Если к элементу нельзя обратиться по порядковому номеру, это явный сигнал разработчику: платформа не берет на себя обязательств по хранению физической последовательности.Часто возникает вопрос: почему Структура иногда кажется более упорядоченной? Рассмотрим различия:
Структура ключами могут быть только строки, соответствующие правилам именования переменных. В некоторых реализациях платформы элементы структуры могут храниться в алфавитном порядке ключей, но документация 1С также не гарантирует стабильность этого порядка в будущих версиях или при передаче между клиентом и сервером.Соответствие гораздо универсальнее: ключом может быть Число, Дата, СправочникСсылка или даже другой объект. Такая универсальность требует более сложных алгоритмов распределения памяти, что делает порядок еще более хаотичным для человеческого глаза.Если в вашей задаче критично знать, в какой последовательности добавлялись данные, проанализируем три основных способа решения проблемы.
Это наиболее производительный метод для случаев, когда нужен и быстрый поиск по ключу (который дает Соответствие), и сохранение порядка (который дает Массив). Мы будем хранить ключи в массиве в порядке их поступления, а значения — в соответствии.
Рассмотрим пример реализации:
// Создаем структуры данных
ОчередьКлючей = Новый Массив;
Данные = Новый Соответствие;
// Добавляем элементы
НовыйКлюч = "ПоступлениеТоваровУслуг";
Если Данные.Получить(НовыйКлюч) = Неопределено Тогда
ОчередьКлючей.Добавить(НовыйКлюч);
КонецЕсли;
Данные.Вставить(НовыйКлюч, "Значение 1");
НовыйКлюч = "_СистемыНалогообложения";
Если Данные.Получить(НовыйКлюч) = Неопределено Тогда
ОчередьКлючей.Добавить(НовыйКлюч);
КонецЕсли;
Данные.Вставить(НовыйКлюч, "Значение 2");
// Правильный обход с сохранением порядка
Для Каждого ТекКлюч Из ОчередьКлючей Цикл
Значение = Данные.Получить(ТекКлюч);
Сообщить(Строка(ТекКлюч) + ": " + Значение);
КонецЦикла;
Если объем данных невелик или требуется хранить несколько связанных параметров, ТаблицаЗначений является идеальным инструментом. Строки в ней всегда располагаются в порядке добавления, если вы явно не вызывали метод Сортировать().
Проанализируем пример:
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Ключ");
ТЗ.Колонки.Добавить("Значение");
// Добавление данных
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Ключ = "Организации";
НоваяСтрока.Значение = "Основная организация";
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Ключ = "_Склады";
НоваяСтрока.Значение = "Центральный склад";
// Обход гарантированно вернет элементы в порядке добавления
Для Каждого Стр Из ТЗ Цикл
Сообщить(Стр.Ключ);
КонецЦикла;
Важное замечание: Поиск в таблице значений методом Найти() работает медленнее (линейный поиск), чем в Соответствии, поэтому для коллекций в десятки тысяч элементов лучше использовать первый способ.
Если вам нужно просто хранить список уникальных или неуникальных значений с представлением и сохранением порядка, используйте СписокЗначений. Он поддерживает методы Добавить() и Вставить(Индекс, ...), что позволяет четко контролировать позицию элемента.
Вернемся к вопросу автора: почему именно символ _ вызвал такой эффект? В программировании строки часто сравниваются по кодам символов. Символ подчеркивания имеет код, отличный от букв алфавита, что влияет на результат хеш-функции. При вставке такого ключа произошло переполнение текущей емкости хеш-таблицы, что спровоцировало процедуру рехеширования. Все элементы «переехали» в новые ячейки памяти в соответствии с новой логикой распределения, что и отразилось на выводе в Сообщить().
Подведем итоги нашего исследования работы с коллекциями в 1С:
Соответствие или Структура. Это «плавающие» структуры данных.Массив или ТаблицаЗначений.Соответствия между клиентом и сервером (через параметры вызова процедур) порядок элементов может измениться снова, так как объект будет пересобран из сериализованного состояния.Используя эти принципы, вы создадите надежный и предсказуемый код, который не будет зависеть от внутренних алгоритмов оптимизации платформы 1С.