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