Если вы только начинаете программировать в 1С:Предприятие или углубляетесь в работу с коллекциями данных, рано или поздно столкнётесь с термином «итератор». На первый взгляд это кажется чем-то сложным и техническим, но на деле итераторы — один из самых удобных инструментов для перебора элементов. Без них пришлось бы писать громоздкие циклы Пока с ручным управлением счётчиками, а с ними — всего пара строк кода.
В этой статье мы разберём, что такое итератор в 1С, как он устроен под капотом платформы, и почему его часто путают с циклом Для каждого. Вы узнаете, в каких случаях итераторы незаменимы, а где их лучше избегать, а также увидите практические примеры с пояснениями. Не бойтесь — даже если вы никогда не писали код на встроенном языке 1С, после прочтения вы сможете уверенно использовать итераторы в своих разработках.
Что такое итератор: определение простыми словами
Итератор (от англ. iterator — «повторитель») — это объект, который позволяет последовательно обходить элементы коллекции, не зная её внутренней структуры. Представьте, что у вас есть список товаров в документе ПоступлениеТоваров. Чтобы перебрать все строки табличной части, можно:
- 🔢 Использовать цикл
Дляс счётчиком от 1 доКоличество()— но это неудобно, если строки добавляются или удаляются динамически. - 🔄 Применить цикл
Покас ручным увеличением индекса — риск ошибок и пропусков. - 🚀 Воспользоваться итератором — он сам «знает», как перемещаться по коллекции, скрывая детали реализации.
В 1С:Предприятие 8 итераторы тесно связаны с циклом Для каждого, но это не одно и то же. Цикл — это синтаксический сахар, а итератор — механизм, который за ним стоит. Например, когда вы пишете:
Для Каждого Товар Из СписокТоваров Цикл
Сообщить(Товар.Наименование);
КонецЦикла;
— платформа 1С автоматически создаёт итератор для коллекции СписокТоваров и управляет им.
Как устроен итератор в платформе 1С: технические детали
С точки зрения внутренней реализации, итератор в 1С — это объект, который поддерживает два ключевых метода:
Следующий()— перемещает «указатель» на следующий элемент коллекции и возвращает его (илиНеопределено, если элементы закончились).Закончился()— проверяет, достигнут ли конец коллекции.
Когда вы используете цикл Для каждого, платформа 1С неявно вызывает эти методы. Но можно работать с итератором и напрямую — это полезно для сложных сценариев, где нужно, например, прервать обход на определённом условии или перемещаться по коллекции в обратном порядке.
| Метод итератора | Описание | Пример использования |
|---|---|---|
Следующий() |
Возвращает следующий элемент или Неопределено |
|
Закончился() |
Возвращает Истина, если элементы закончились |
|
Сбросить() |
Возвращает указатель в начало коллекции | |
Важно: итераторы в 1С не поддерживают модификацию коллекции во время обхода (например, удаление элементов). Это может привести к ошибке «Коллекция была изменена». В таких случаях используйте цикл Для с обратным порядком или создайте копию коллекции.
Если вам нужно удалить элементы из коллекции во время перебора, сначала соберите их в отдельный список, а затем удалите за пределами цикла. Это безопаснее, чем модифицировать коллекцию "на лету".
Цикл «Для каждого» vs итератор: в чём разница?
Многие разработчики 1С ошибочно считают, что цикл Для каждого и итератор — это одно и то же. На самом деле:
- 🔹 Цикл
Для каждого— это синтаксический сахар, упрощающий запись. Он автоматически создаёт итератор и управляет им. - 🔹 Итератор — это объект, который можно использовать самостоятельно, например, для нестандартного обхода коллекции.
Пример: если вам нужно перебрать элементы массива, но пропустить первые 5, с циклом Для каждого это сделать неудобно. А с итератором — легко:
Итератор = Массив.ПолучитьИтератор();
Для Сч = 1 По 5 Цикл
Итератор.Следующий(); // Пропускаем первые 5 элементов
КонецЦикла;
Пока Не Итератор.Закончился() Цикл
Элемент = Итератор.Следующий();
Сообщить(Элемент);
КонецЦикла;
Ещё одно ключевое отличие: цикл Для каждого не позволяет прервать обход на середине (кроме как оператором Прервать), тогда как с итератором вы можете гибко управлять процессом, например, возвращаться к началу коллекции методом Сбросить().
Когда лучше использовать итератор вместо цикла "Для каждого"?
Итераторы незаменимы, если вам нужно:
1. Перемещаться по коллекции в произвольном порядке (например, назад).
2. Прерывать обход по сложному условию, которое нельзя выразить в теле цикла.
3. Работать с несколькими коллекциями параллельно (например, сравнивать элементы двух массивов).
4. Оптимизировать производительность в больших коллекциях (итераторы могут быть быстрее для некоторых структур данных).
Практические примеры использования итераторов в 1С
Разберём несколько реальных задач, где итераторы упрощают код или решают проблемы, с которыми циклы Для каждого не справляются.
Пример 1: Поиск дубликатов в справочнике
Допустим, вам нужно найти все дублирующиеся наименования в справочнике Номенклатура. С итератором это можно сделать так:
Выборка = Справочники.Номенклатура.Выбрать();
Итератор = Выборка.ПолучитьИтератор();
СписокНаименований = Новый СписокЗначений;
Пока Не Итератор.Закончился() Цикл
Элемент = Итератор.Следующий();
Если СписокНаименований.Найти(Элемент.Наименование) <> Неопределено Тогда
Сообщить("Дубликат: " + Элемент.Наименование);
Иначе
СписокНаименований.Добавить(Элемент.Наименование);
КонецЕсли;
КонецЦикла;
Пример 2: Обход табличной части документа в обратном порядке
Иногда требуется перебрать строки табличной части с конца (например, для корректного удаления). Итератор здесь не подходит, но можно использовать комбинацию с методом Количество():
Док = Документы.ПоступлениеТоваров.НайтиПоНомеру("0001");
ТабличнаяЧасть = Док.Товары;
Для Сч = ТабличнаяЧасть.Количество() По 1 Цикл
Строка = ТабличнаяЧасть[Сч - 1];
// Обработка строки
КонецЦикла;
✅ Нужно гибко управлять обходом (пропускать элементы, возвращаться назад)
✅ Работаете с большими коллекциями (оптимизация производительности)
✅ Требуется параллельный обход нескольких коллекций
❌ Не используйте итераторы для простых переборов — лучше цикл "Для каждого"
-->
Ошибки при работе с итераторами и как их избежать
Несмотря на удобство, итераторы таят несколько подводных камней, о которых стоит знать:
⚠️ Внимание: Если вы модифицируете коллекцию (добавляете/удаляете элементы) во время обхода с помощью итератора, платформа 1С выбросит исключение «Коллекция была изменена». Это связано с тем, что итератор хранит внутреннее состояние коллекции на момент создания.Типичные ошибки:
- 🔴 Изменение коллекции в цикле: Удаление элемента из массива или таблицы значений, по которому идёт обход.
- 🔴 Использование итератора после его завершения: Вызов
Следующий()после того, какЗакончился()вернулИстина, приведёт к ошибке.- 🔴 Несброшенный итератор: Если вы используете один итератор для нескольких обходов, не забывайте вызывать
Сбросить().Пример ошибочного кода:
Массив = Новый Массив();Массив.Добавить(1);
Массив.Добавить(2);
Итератор = Массив.ПолучитьИтератор();
Пока Не Итератор.Закончился() Цикл
Элемент = Итератор.Следующий();
Если Элемент = 1 Тогда
Массив.Удалить(0); // ОШИБКА: коллекция изменилась!
КонецЕсли;
КонецЦикла;
Чтобы избежать проблемы, создайте копию коллекции:
КопияМассива = Массив.Скопировать();Итератор = КопияМассива.ПолучитьИтератор();
Всегда проверяйте состояние итератора методом
Закончился()перед вызовомСледующий(), чтобы избежать ошибок доступа к несуществующим элементам.Производительность: итераторы vs традиционные циклы
Один из частых вопросов: что быстрее — итератор или цикл
Дляс счётчиком? Ответ зависит от типа коллекции и размера данных:
Тип коллекции Итератор Цикл "Для" Цикл "Для каждого" Массив Средняя скорость ⚡ Самый быстрый (прямой доступ по индексу) Быстрее итератора Список значений ⚡ Быстрее цикла "Для" Медленнее (требует вычисления индексов) Сопоставимо с итератором Таблица значений Оптимально для больших таблиц Медленно при большом количестве строк Удобно, но проигрывает итератору в скорости Для критичных по производительности участков кода (например, обработка тысяч строк в документах) рекомендуется:
- 📊 Для массивов использовать цикл
Дляс индексами.- 📋 Для списков значений и таблиц значений — итераторы или цикл
Для каждого.- 🔍 Для запросов и выборок — итераторы (они оптимизированы для работы с данными из базы).
Тест на производительность: Если вам нужно перебрать 10 000 строк в таблице значений, итератор сделает это примерно на 15–20% быстрее, чем цикл
Для каждого, благодаря меньшему количеству внутренних проверок.Итераторы в типичных задачах 1С: бухгалтерия, торговля, кадры
Давайте посмотрим, как итераторы применяются в реальных бизнес-задачах на платформе 1С:Предприятие.
1. Обновление цен номенклатуры по правилам
Допустим, вам нужно обновить цены в справочнике
Номенклатурана основе данных из внешнего файла. Итератор поможет эффективно сравнить элементы:ВыборкаНоменклатуры = Справочники.Номенклатура.Выбрать();ИтераторНоменклатуры = ВыборкаНоменклатуры.ПолучитьИтератор();
Пока Не ИтераторНоменклатуры.Закончился() Цикл
Товар = ИтераторНоменклатуры.Следующий();
НоваяЦена = ПолучитьЦенуИзФайла(Товар.Артикул); // Ваша функция
Если НоваяЦена <> Неопределено Тогда
Товар.Цена = НоваяЦена;
Товар.Записать();
КонецЕсли;
КонецЦикла;
2. Поиск расхождений в документах
При сверке данных между двумя документами (например,
ПоступлениеТоваровиОприходованиеТоваров) итераторы позволяют синхронизировать обход:ИтераторПоступления = ДокПоступление.Товары.ПолучитьИтератор();ИтераторОприходования = ДокОприходование.Товары.ПолучитьИтератор();
Пока Не ИтераторПоступления.Закончился() И Не ИтераторОприходования.Закончился() Цикл
СтрокаПоступления = ИтераторПоступления.Следующий();
СтрокаОприходования = ИтераторОприходования.Следующий();
Если СтрокаПоступления.Номенклатура <> СтрокаОприходования.Номенклатура Тогда
Сообщить("Расхождение по номенклатуре!");
КонецЕсли;
КонецЦикла;
3. Формирование отчётов с группировкой
При построении отчётов с группировкой данных (например, по контрагентам) итераторы помогают управлять иерархией:
Группы = Новый Структура();Для Каждого Строка Из ТаблицаДанных Цикл
Если Незначение(Группы[Строка.Контрагент]) Тогда
Группы[Строка.Контрагент] = Новый СписокЗначений;
КонецЕсли;
Группы[Строка.Контрагент].Добавить(Строка);
КонецЦикла;
// Обход групп с итератором
Для Каждого Контрагент Из Группы Цикл
ИтераторСтрок = Контрагент.Значение.ПолучитьИтератор();
Пока Не ИтераторСтрок.Закончился() Цикл
// Обработка строк группы
КонецЦикла;
КонецЦикла;
В 1С:Зарплата и Управление Персоналом итераторы часто применяют для обхода списков сотрудников при расчёте зарплаты или проверке данных по табелям.
FAQ: Частые вопросы об итераторах в 1С
Можно ли использовать итератор для обхода результата запроса?
Да, но с оговорками. Результат запроса в 1С — это специальный объект
РезультатЗапроса, который поддерживает методПолучитьИтератор(). Однако для выборок чаще используют циклДля каждогоили методВыгрузить(), так как они проще в применении. Итератор здесь нужен только для нестандартных сценариев, например, параллельного обхода нескольких выборок.Чем отличается итератор от перечисления (enum) в других языках программирования?
Итератор в 1С ближе всего к концепции iterator в C++ или Java, но проще. В отличие от перечислений (enum), которые представляют набор констант, итераторы в 1С — это динамические объекты для обхода коллекций. Они не хранят данные, а только управляют доступом к ним.
Как обойти коллекцию в обратном порядке с помощью итератора?
Стандартный итератор в 1С не поддерживает обратный обход. Для этого нужно:
- Создать цикл
ДляотКоличество() - 1до0.- Использовать доступ по индексу:
Коллекция[Индекс].Пример:
Для Индекс = Массив.Количество() - 1 По 0 ЦиклСообщить(Массив[Индекс]);
КонецЦикла;
Можно ли создать свой класс-итератор в 1С?
Технически да, но это требует глубоких знаний встроенного языка. Для этого нужно реализовать методы
Следующий(),Закончился()иСбросить()в своём классе. Однако на практике это редко востребовано, так как стандартные итераторы покрывают 99% задач.Почему при использовании итератора иногда возникает ошибка «Коллекция была изменена»?
Эта ошибка появляется, если вы модифицируете коллекцию (добавляете/удаляете элементы) во время обхода. Итератор хранит «снимок» состояния коллекции на момент создания, и любые изменения нарушают его внутреннюю логику. Решение:
- Создайте копию коллекции перед обходом.
- Используйте цикл
Дляс обратным порядком для удаления элементов.