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

Цикл for-each

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

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.
Пока есть еще элементы

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

14
Задача
Java Syntax Pro, 14 уровень, 2 лекция
Недоступна
Выводим в консоли элементы множества
В классе Solution есть метод print(HashSet), который должен выводить в консоли все элементы множества, используя iterator(). Метод main не участвует в проверке.

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);
}

14
Задача
Java Syntax Pro, 14 уровень, 2 лекция
Недоступна
Переводим итератор в цикл for-each
В классе Solution есть методы printHashSet(HashSet) и printList(ArrayList), которые выводят все элементы из переданных коллекций — списка и множества — с новой строки. Твоя задача переписать методы на использование цикла for-each. Метод main не участвует в проверке.

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.

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

14
Задача
Java Syntax Pro, 14 уровень, 2 лекция
Недоступна
Найти и обезвредить
В классе Solution объявлены методы, которые тебе нужно реализовать следующим образом: 1. removeBugWithFor(ArrayList) - должен удалить строку из списка, если она содержит слово bug, используя цикл for и счетчик. 2. removeBugWithWhile(ArrayList) - должен удалить строку из списка, если
Комментарии (421)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Anonymous #3585174 Уровень 16
2 июля 2025
Like
Aura Уровень 18
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!), что мягко говоря - плохо)
VadChet Уровень 28
14 мая 2025
В комиксе отсылка на Евангелион☺️
Alina Gabidulina Уровень 19
29 мая 2025
и в чем же она выражена?
VadChet Уровень 28
6 июня 2025
Сергей Сак Уровень 13
7 мая 2025
Для тех, кто кодит в идее. Чтобы быстро создать цикл for-each, введите iter и нажмите Tab
Danya Уровень 17
15 апреля 2025
Спустя сколько времени я узнаю о цикле for eche главное раньше в ответах в разных прошлых задачах он встречался а изучаем только сейчас
Danya Уровень 17
15 апреля 2025
блин не догадался использовать метод equalsIgnoreCase😭
Anonymous #3239927 Уровень 5
7 апреля 2025
Статья не понравилась, делается акцент "на выводе" при объяснении для чего нужен Iterator, зачем? Мы и так вывести можем с sout, метод toString() реализован в Set, другой вопрос можно обратить внимание, что Iterator нужен для "управляемого обхода множества", но для этого явно надо переписать эту статью
Anonymouse #3523256 Уровень 25
17 февраля 2025
👩‍🏫(лектор): Вы уже познакомились со множеством HashSet. И если вы действительно разбирались с ним, а не просто читали лекцию, должны были задаться вопросом... 👨‍🎓(я): 😳😬😅
Anton Zhukov Уровень 12
4 марта 2025
факты
Евгений Уровень 34
11 марта 2025
Аргументы
Davyd Уровень 30
5 февраля 2025
Наконец-то, нам решили рассказать спустя 10 лекций про цикл for-each.
LisoEdoEd Уровень 16 Student
10 мая 2025
Из-за этого он усвоился лучше
Павел К. Уровень 33
4 февраля 2025
А как всё же правильнее, фиксированную константу "BUG" сравнивать с переменными или переменные с константой? Я обычно константу с переменной сравниваю: "BUG".equalsIgnoreCase(str)