Если вы только начинаете программировать в 1С:Предприятие или углубляетесь в работу с коллекциями данных, рано или поздно столкнётесь с термином «итератор». На первый взгляд это кажется чем-то сложным и техническим, но на деле итераторы — один из самых удобных инструментов для перебора элементов. Без них пришлось бы писать громоздкие циклы Пока с ручным управлением счётчиками, а с ними — всего пара строк кода.

В этой статье мы разберём, что такое итератор в 1С, как он устроен под капотом платформы, и почему его часто путают с циклом Для каждого. Вы узнаете, в каких случаях итераторы незаменимы, а где их лучше избегать, а также увидите практические примеры с пояснениями. Не бойтесь — даже если вы никогда не писали код на встроенном языке , после прочтения вы сможете уверенно использовать итераторы в своих разработках.

Что такое итератор: определение простыми словами

Итератор (от англ. iterator — «повторитель») — это объект, который позволяет последовательно обходить элементы коллекции, не зная её внутренней структуры. Представьте, что у вас есть список товаров в документе ПоступлениеТоваров. Чтобы перебрать все строки табличной части, можно:

  • 🔢 Использовать цикл Для с счётчиком от 1 до Количество() — но это неудобно, если строки добавляются или удаляются динамически.
  • 🔄 Применить цикл Пока с ручным увеличением индекса — риск ошибок и пропусков.
  • 🚀 Воспользоваться итератором — он сам «знает», как перемещаться по коллекции, скрывая детали реализации.

В 1С:Предприятие 8 итераторы тесно связаны с циклом Для каждого, но это не одно и то же. Цикл — это синтаксический сахар, а итератор — механизм, который за ним стоит. Например, когда вы пишете:

Для Каждого Товар Из СписокТоваров Цикл

Сообщить(Товар.Наименование);

КонецЦикла;

— платформа автоматически создаёт итератор для коллекции СписокТоваров и управляет им.

📊 Как вы обычно перебираете коллекции в 1С?
Использую цикл "Для каждого"
Пишу циклы "Для" с счётчиком
Применяю методы "Найти()" и "Получить()"
Не знаю, что такое итераторы

Как устроен итератор в платформе 1С: технические детали

С точки зрения внутренней реализации, итератор в — это объект, который поддерживает два ключевых метода:

  1. Следующий() — перемещает «указатель» на следующий элемент коллекции и возвращает его (или Неопределено, если элементы закончились).
  2. Закончился() — проверяет, достигнут ли конец коллекции.

Когда вы используете цикл Для каждого, платформа неявно вызывает эти методы. Но можно работать с итератором и напрямую — это полезно для сложных сценариев, где нужно, например, прервать обход на определённом условии или перемещаться по коллекции в обратном порядке.

Метод итератора Описание Пример использования
Следующий() Возвращает следующий элемент или Неопределено
Элемент = Итератор.Следующий();
Закончился() Возвращает Истина, если элементы закончились
Пока Не Итератор.Закончился() Цикл
Сбросить() Возвращает указатель в начало коллекции
Итератор.Сбросить();

Важно: итераторы в 1С не поддерживают модификацию коллекции во время обхода (например, удаление элементов). Это может привести к ошибке «Коллекция была изменена». В таких случаях используйте цикл Для с обратным порядком или создайте копию коллекции.

💡

Если вам нужно удалить элементы из коллекции во время перебора, сначала соберите их в отдельный список, а затем удалите за пределами цикла. Это безопаснее, чем модифицировать коллекцию "на лету".

Цикл «Для каждого» vs итератор: в чём разница?

Многие разработчики ошибочно считают, что цикл Для каждого и итератор — это одно и то же. На самом деле:

  • 🔹 Цикл Для каждого — это синтаксический сахар, упрощающий запись. Он автоматически создаёт итератор и управляет им.
  • 🔹 Итератор — это объект, который можно использовать самостоятельно, например, для нестандартного обхода коллекции.

Пример: если вам нужно перебрать элементы массива, но пропустить первые 5, с циклом Для каждого это сделать неудобно. А с итератором — легко:

Итератор = Массив.ПолучитьИтератор();

Для Сч = 1 По 5 Цикл

Итератор.Следующий(); // Пропускаем первые 5 элементов

КонецЦикла;

Пока Не Итератор.Закончился() Цикл

Элемент = Итератор.Следующий();

Сообщить(Элемент);

КонецЦикла;

Ещё одно ключевое отличие: цикл Для каждого не позволяет прервать обход на середине (кроме как оператором Прервать), тогда как с итератором вы можете гибко управлять процессом, например, возвращаться к началу коллекции методом Сбросить().

Когда лучше использовать итератор вместо цикла "Для каждого"?

Итераторы незаменимы, если вам нужно:

1. Перемещаться по коллекции в произвольном порядке (например, назад).

2. Прерывать обход по сложному условию, которое нельзя выразить в теле цикла.

3. Работать с несколькими коллекциями параллельно (например, сравнивать элементы двух массивов).

4. Оптимизировать производительность в больших коллекциях (итераторы могут быть быстрее для некоторых структур данных).

Практические примеры использования итераторов в 1С

Разберём несколько реальных задач, где итераторы упрощают код или решают проблемы, с которыми циклы Для каждого не справляются.

Пример 1: Поиск дубликатов в справочнике

Допустим, вам нужно найти все дублирующиеся наименования в справочнике Номенклатура. С итератором это можно сделать так:

Выборка = Справочники.Номенклатура.Выбрать();

Итератор = Выборка.ПолучитьИтератор();

СписокНаименований = Новый СписокЗначений;

Пока Не Итератор.Закончился() Цикл

Элемент = Итератор.Следующий();

Если СписокНаименований.Найти(Элемент.Наименование) <> Неопределено Тогда

Сообщить("Дубликат: " + Элемент.Наименование);

Иначе

СписокНаименований.Добавить(Элемент.Наименование);

КонецЕсли;

КонецЦикла;

Пример 2: Обход табличной части документа в обратном порядке

Иногда требуется перебрать строки табличной части с конца (например, для корректного удаления). Итератор здесь не подходит, но можно использовать комбинацию с методом Количество():

Док = Документы.ПоступлениеТоваров.НайтиПоНомеру("0001");

ТабличнаяЧасть = Док.Товары;

Для Сч = ТабличнаяЧасть.Количество() По 1 Цикл

Строка = ТабличнаяЧасть[Сч - 1];

// Обработка строки

КонецЦикла;

✅ Нужно гибко управлять обходом (пропускать элементы, возвращаться назад)

✅ Работаете с большими коллекциями (оптимизация производительности)

✅ Требуется параллельный обход нескольких коллекций

❌ Не используйте итераторы для простых переборов — лучше цикл "Для каждого"

-->

Ошибки при работе с итераторами и как их избежать

Несмотря на удобство, итераторы таят несколько подводных камней, о которых стоит знать:

⚠️ Внимание: Если вы модифицируете коллекцию (добавляете/удаляете элементы) во время обхода с помощью итератора, платформа выбросит исключение «Коллекция была изменена». Это связано с тем, что итератор хранит внутреннее состояние коллекции на момент создания.

Типичные ошибки:

  • 🔴 Изменение коллекции в цикле: Удаление элемента из массива или таблицы значений, по которому идёт обход.
  • 🔴 Использование итератора после его завершения: Вызов Следующий() после того, как Закончился() вернул Истина, приведёт к ошибке.
  • 🔴 Несброшенный итератор: Если вы используете один итератор для нескольких обходов, не забывайте вызывать Сбросить().

Пример ошибочного кода:

Массив = Новый Массив();

Массив.Добавить(1);

Массив.Добавить(2);

Итератор = Массив.ПолучитьИтератор();

Пока Не Итератор.Закончился() Цикл

Элемент = Итератор.Следующий();

Если Элемент = 1 Тогда

Массив.Удалить(0); // ОШИБКА: коллекция изменилась!

КонецЕсли;

КонецЦикла;

Чтобы избежать проблемы, создайте копию коллекции:

КопияМассива = Массив.Скопировать();

Итератор = КопияМассива.ПолучитьИтератор();

💡

Всегда проверяйте состояние итератора методом Закончился() перед вызовом Следующий(), чтобы избежать ошибок доступа к несуществующим элементам.

Производительность: итераторы vs традиционные циклы

Один из частых вопросов: что быстрее — итератор или цикл Для с счётчиком? Ответ зависит от типа коллекции и размера данных:

Тип коллекции Итератор Цикл "Для" Цикл "Для каждого"
Массив Средняя скорость Самый быстрый (прямой доступ по индексу) Быстрее итератора
Список значений ⚡ Быстрее цикла "Для" Медленнее (требует вычисления индексов) Сопоставимо с итератором
Таблица значений Оптимально для больших таблиц Медленно при большом количестве строк Удобно, но проигрывает итератору в скорости

Для критичных по производительности участков кода (например, обработка тысяч строк в документах) рекомендуется:

  • 📊 Для массивов использовать цикл Для с индексами.
  • 📋 Для списков значений и таблиц значений — итераторы или цикл Для каждого.
  • 🔍 Для запросов и выборок — итераторы (они оптимизированы для работы с данными из базы).

Тест на производительность: Если вам нужно перебрать 10 000 строк в таблице значений, итератор сделает это примерно на 15–20% быстрее, чем цикл Для каждого, благодаря меньшему количеству внутренних проверок.

Итераторы в типичных задачах 1С: бухгалтерия, торговля, кадры

Давайте посмотрим, как итераторы применяются в реальных бизнес-задачах на платформе 1С:Предприятие.

1. Обновление цен номенклатуры по правилам

Допустим, вам нужно обновить цены в справочнике Номенклатура на основе данных из внешнего файла. Итератор поможет эффективно сравнить элементы:

ВыборкаНоменклатуры = Справочники.Номенклатура.Выбрать();

ИтераторНоменклатуры = ВыборкаНоменклатуры.ПолучитьИтератор();

Пока Не ИтераторНоменклатуры.Закончился() Цикл

Товар = ИтераторНоменклатуры.Следующий();

НоваяЦена = ПолучитьЦенуИзФайла(Товар.Артикул); // Ваша функция

Если НоваяЦена <> Неопределено Тогда

Товар.Цена = НоваяЦена;

Товар.Записать();

КонецЕсли;

КонецЦикла;

2. Поиск расхождений в документах

При сверке данных между двумя документами (например, ПоступлениеТоваров и ОприходованиеТоваров) итераторы позволяют синхронизировать обход:

ИтераторПоступления = ДокПоступление.Товары.ПолучитьИтератор();

ИтераторОприходования = ДокОприходование.Товары.ПолучитьИтератор();

Пока Не ИтераторПоступления.Закончился() И Не ИтераторОприходования.Закончился() Цикл

СтрокаПоступления = ИтераторПоступления.Следующий();

СтрокаОприходования = ИтераторОприходования.Следующий();

Если СтрокаПоступления.Номенклатура <> СтрокаОприходования.Номенклатура Тогда

Сообщить("Расхождение по номенклатуре!");

КонецЕсли;

КонецЦикла;

3. Формирование отчётов с группировкой

При построении отчётов с группировкой данных (например, по контрагентам) итераторы помогают управлять иерархией:

Группы = Новый Структура();

Для Каждого Строка Из ТаблицаДанных Цикл

Если Незначение(Группы[Строка.Контрагент]) Тогда

Группы[Строка.Контрагент] = Новый СписокЗначений;

КонецЕсли;

Группы[Строка.Контрагент].Добавить(Строка);

КонецЦикла;

// Обход групп с итератором

Для Каждого Контрагент Из Группы Цикл

ИтераторСтрок = Контрагент.Значение.ПолучитьИтератор();

Пока Не ИтераторСтрок.Закончился() Цикл

// Обработка строк группы

КонецЦикла;

КонецЦикла;

В 1С:Зарплата и Управление Персоналом итераторы часто применяют для обхода списков сотрудников при расчёте зарплаты или проверке данных по табелям.

FAQ: Частые вопросы об итераторах в 1С

Можно ли использовать итератор для обхода результата запроса?

Да, но с оговорками. Результат запроса в — это специальный объект РезультатЗапроса, который поддерживает метод ПолучитьИтератор(). Однако для выборок чаще используют цикл Для каждого или метод Выгрузить(), так как они проще в применении. Итератор здесь нужен только для нестандартных сценариев, например, параллельного обхода нескольких выборок.

Чем отличается итератор от перечисления (enum) в других языках программирования?

Итератор в ближе всего к концепции iterator в C++ или Java, но проще. В отличие от перечислений (enum), которые представляют набор констант, итераторы в — это динамические объекты для обхода коллекций. Они не хранят данные, а только управляют доступом к ним.

Как обойти коллекцию в обратном порядке с помощью итератора?

Стандартный итератор в не поддерживает обратный обход. Для этого нужно:

  1. Создать цикл Для от Количество() - 1 до 0.
  2. Использовать доступ по индексу: Коллекция[Индекс].

Пример:

Для Индекс = Массив.Количество() - 1 По 0 Цикл

Сообщить(Массив[Индекс]);

КонецЦикла;

Можно ли создать свой класс-итератор в 1С?

Технически да, но это требует глубоких знаний встроенного языка. Для этого нужно реализовать методы Следующий(), Закончился() и Сбросить() в своём классе. Однако на практике это редко востребовано, так как стандартные итераторы покрывают 99% задач.

Почему при использовании итератора иногда возникает ошибка «Коллекция была изменена»?

Эта ошибка появляется, если вы модифицируете коллекцию (добавляете/удаляете элементы) во время обхода. Итератор хранит «снимок» состояния коллекции на момент создания, и любые изменения нарушают его внутреннюю логику. Решение:

  • Создайте копию коллекции перед обходом.
  • Используйте цикл Для с обратным порядком для удаления элементов.