JavaRush /Курсы /Java Syntax Pro /Цикл for-each

Цикл for-each

Java Syntax Pro
13 уровень , 1 лекция
Открыта

1. Предыстория появления итератора

Вы уже познакомились со множеством HashSet. И если вы действительно разбирались с ним, а не просто читали лекцию, должны были задаться вопросом:

А как вывести список всех элементов HashSet на экран? Ведь методов get() и set() у множества нет!

И HashSet в своей проблеме не одинок. Кроме HashSet, есть еще много различных коллекций, у которых нельзя получить элемент по номеру, ведь четкий порядок элементов отсутствует.

Программисты в свое время изобрели много сложных структур данных, таких как граф, дерево. Или, например, список списков.

Многие контейнеры меняют порядок своих элементов при добавлении новых или удалении существующих элементов. Например, есть список, который хранит элементы в отсортированном порядке, и при добавлении нового элемента он практически всегда вставляется в середину списка.

Вот мы и получаем ситуацию, что контейнер, содержащий элементы, есть, сами элементы тоже есть, а фиксированного порядка нет.

Но допустим, мы хотим скопировать все элементы из такой коллекции в массив или список. Нам нужно получить все элементы. Нам все равно в каком порядке мы их обойдем, главное — не повторяться. Как нам это сделать?


2. Итератор в коллекции

В качестве решения вышеописанной проблемы было предложено решение — итератор.

Итератор — это специальный объект у коллекции, который помогает обойти все элементы коллекции и не повторяться.

Получить итератор у любой коллекции можно с помощью кода:

Iterator<Тип> it = имя.iterator();

Где имя — это имя переменной-коллекции, Тип — это тип элементов коллекции. iterator() — это метод коллекции. it — это имя переменной-объекта-итератора.

У объекта-итератора есть 3 метода:

Метод Описание
Тип next()
Возвращает очередной элемент коллекции
boolean hasNext()
Проверяет, есть ли еще не пройденные элементы
void remove()
Удаляет текущий элемент коллекции

Эти методы чем-то похожи на методы класса Scanner: nextInt() и hasNextInt().

Метод next() возвращает очередной элемент коллекции, у которой мы получили итератор.

Метод hasNext() проверяет, есть ли еще элементы в коллекции, которые итератор не вернул.

Вот как можно вывести на экран все элементы множества HashSet:

Код Примечания
HashSet<String> set = new HashSet<String>();

set.add("Привет");
set.add("Hello");
set.add("Hola");
set.add("Bonjour");
set.add("Ciao");
set.add("Namaste");

Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
Создаем объект типа HashSet, который хранит элементы типа String.


Заносим в set приветствия на разных языках.




Получаем объект-итератор у множества set.
Пока есть еще элементы

Получаем следующий элемент
Выводим элемент на экран


3. Цикл for-each

Основной минус итератора в том, что с его использованием код получается еще более громоздким, чем с использованием цикла for.

Давайте для сравнения выведем на экран список с помощью цикла for и с помощью итератора:

Итератор Цикл for
ArrayList<String> list = new ArrayList<String>();

Iterator<String> it = list.iterator();
while (it.hasNext())
{
 String str = it.next();
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}

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

Однако разработчики Java вновь решили подсыпать нам сахарку. И на наше счастье, это опять был синтаксический сахар.

Они добавили в Java новый вид циклов и назвали его for-each. Вот как выглядит его использование в общем случае:

for(Тип имя:коллекция)

Где коллекция — это имя переменной коллекции, Тип — это тип элементов коллекции, а имя — это имя переменной, которая на каждом витке цикла принимает очередное значение из коллекции.

Этот цикл обходит все элементы коллекции с помощью скрытого итератора. Вот как он на самом деле работает:

Цикл for-each Что видит компилятор: Цикл с итератором
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();
Iterator<String> it = list.iterator();

while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}

Когда компилятор встретит в вашем коде цикл for-each, он просто заменит его на код справа: добавит метод получения итератора и все недостающие вызовы методов.

Программисты очень любят цикл for-each и практически всегда используют его, когда нужно обойти все элементы коллекции.

Даже обход списка ArrayList с помощью цикла for-each выглядит короче:

Цикл for-each Цикл for
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}


4. Удаление элемента в цикле for-each

У цикла for-each есть один минус: он не умеет правильно удалять элементы. Если вы напишете такой код, получите ошибку.

Код Примечание
ArrayList<String> list = new ArrayList<String>();

list.add("Привет");
list.add("Hello");
list.add("Hola");
list.add("Bonjour");
list.add("Ciao");
list.add("Namaste");

for (String str: list)
{
   if (str.equals("Hello"))
      list.remove(str);
}












При удалении возникнет ошибка!

Это очень красивый и понятный код, только работать он не будет.

Важно!

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

Есть три способа обойти это ограничение.

1 Использование другого цикла

Если вы обходите коллекцию ArrayList, можете воспользоваться обычным циклом со счетчиком i.

Код
for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);

   if (str.equals("Hello"))
   {
      list.remove(str);
      i--;    // нужно уменьшить i, т.к. после удаления элементы сдвинулись
   }
}

Однако этот вариант не подходит для коллекций HashSet и HashMap

2 Явное использование итератора

Можно использовать итератор явно и задействовать его метод remove().

Рабочий вариант Нерабочий вариант
Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   if (str.equals("Hello"))
       it.remove();
}

for (String str: list) { if (str.equals("Hello")) list.remove(str); }

Обратите внимание, что метод remove() мы вызываем у объекта-итератора! Итератор «будет знать» об удалении элемента и сможет правильно обработать эту ситуацию.

3 Использование копии коллекции

Также вы можете создать копию коллекции и использовать в цикле for-each коллекцию-копию, а удалять элементы из оригинальной коллекции.

Код Примечание
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
Копию коллекции создать очень легко



Цикл использует итератор коллекции-копии.
Элементы удаляются из коллекции list.

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

Комментарии (428)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Кирилл Уровень 28
7 ноября 2025
я понял как лекции проходить, надо не сверху вниз читать, а снизу вверх. очень много инфы даётся именно в видосах. парадокс блин
Anonymous #3513704 Уровень 24
20 октября 2025
Ребят уж определитесь, Итератор это объект или интерфейс?...
Pukidmi Уровень 7
31 октября 2025
Еще и паттерн... Я так понимаю что Iterator это интерфейс, а создать объект интерфейса нельзя. Метод iterator() интерфейса Iterable, переопределен в классе ArrayList и возвращает конкретную реализацию интерфейса Iterator, там что-то сложное типа приватный внутренний класс ArrayList.Itr. В общем это как List - интерфейс, а реализация - ArrayList.
Anton Zorin Уровень 20
5 сентября 2025
Разве строка содержит и строка, так сказать, эквивалентна не разные вещи? Правильное решение не удалит строку "Hello Bug!". Тогда уже надо было писать - удалить строку "bug", которая содержится в списке.
HXTSUKX6E Уровень 24
24 августа 2025
Задача про реализацию методов с удалением Такое работать будет! while (iterator.hasNext()) { String it = iterator.next(); if (it.equalsIgnoreCase("bug")) iterator.remove(); } а вот такое нет! while (iterator.hasNext()) { iterator.next(); if (iterator.next().equalsIgnoreCase("bug")) iterator.remove(); } Комментарий: как я понимаю из-за того, что при вызове метода next() он уже перескакивает на следующий элемент) (Если у кого-то есть более хорошее объяснение - отвечайте на коммент)
Anonymous #3585174 Уровень 33
2 июля 2025
Like
Aura Уровень 23
21 мая 2025
Мне гптшка по рукам била, когда я удалял через первый вариант, всегда говорила, что легко могут быть ситуация, когда все по одному месту пойдет, и советовала, если и удалять элементы коллекции через обычный for, то идти с конца, ведь при удалении объекты смещаются налево или использовать Iterator.
Singleton Уровень 15
30 июня 2025
По одному месту всё вряд ли пойдет, но по сути если удалять через

for (int i = 0; i < list.size(); i++) {
	String str = list.get(i); 
	if (str.equals("Hello")) {
		list.remove(str);
		i--; // нужно уменьшить i, т.к. после удаления элементы сдвинулись 
	} 
}
То, в случае когда нужно будет удалить бОльшую часть элементов (например если все элементы "Hello"). На каждой итерации, элементы внутри массива будут сдвигаться влево на один, что даст сложность подобного удаления O(n!), что мягко говоря - плохо)
@itArarat Уровень 26
29 июля 2025
да ладно? об этом же говорилось в лекциях про ArrayList
VadChet Уровень 29
14 мая 2025
В комиксе отсылка на Евангелион☺️
Alina Gabidulina Уровень 15
29 мая 2025
и в чем же она выражена?
VadChet Уровень 29
6 июня 2025
Сергей Сак Уровень 16
7 мая 2025
Для тех, кто кодит в идее. Чтобы быстро создать цикл for-each, введите iter и нажмите Tab
Danya Уровень 17
15 апреля 2025
Спустя сколько времени я узнаю о цикле for eche главное раньше в ответах в разных прошлых задачах он встречался а изучаем только сейчас
Danya Уровень 17
15 апреля 2025
блин не догадался использовать метод equalsIgnoreCase😭