TGViewer
Java: fill the gaps Java: fill the gaps @java_fillthegaps · 12.3K subscribers
Post #296 5.1K
Разница между for и forEach

довольно проста. for использует для обхода итератор, а forEach - траверс.

В этом посте разберём разницу между ними. Цель — чисто познавательная: как компьютер саенс проявляется в языке программирования.

Сразу скажу: всё это имеет смысл только для обхода многопоточных структур данных. Если мы попробуем изменить ArrayList, HashMap и т.д во время обхода, то получим ConcurrentModificationException.

Начнём издалека и посмотрим на обход более сложной сущности — графа. Граф состоит из вершин и рёбер, в нём могут быть циклы.

Обойти все вершины графа по одному разу (найти гамильтонов путь) — непростая задача. Алгоритм ведёт учёт посещённых вершин и перебирает множество вариантов. В computer science обход структуры данных называется traverse.
🔸Самое важное: следующий элемент вычисляется только, когда понадобится.

Вернёмся в мир java. В стандартных структурах царят списки, множества, очереди и деревья. Циклов здесь нет, следующий элемент при обходе однозначен, а его отсутствие означает конец работы. Поэтому обход выглядит так:
Iterator it=list.iterator();
while(it.hasNext())
int result = it.next();

Метод next возвращает текущий элемент и сдвигает указатель на следующий. Метод hasNext проверяет, ссылается ли этот указатель куда-нибудь. Этот паттерн повторяется снова и снова и называется Iteration.

Итератор лежит в основе синтаксиса for (T e: collection)

🔸Самое важное: указатель на следующий элемент вычисляется заранее.

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

Что по задаче:

Обход через for использует итератор. Указатель на следующий элемент вычисляется заранее. Более подходящий новый элемент не отображается, и выводится 2 элемента.

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

❓Почему нельзя использовать траверс по умолчанию?
— Итератор проще и работает быстрее, а условия для пропуска элемента при обходе встречаются редко.

❓Зачем нужно несколько вариантов?
— ConcurrentHashMap может перестраиваться во время обхода. Чтобы во время перестройки не выводить дубликаты, используется траверс со сложной логикой.

Итого: при выводе элементов ConcurrentHashMap через for и forEach используются разные алгоритмы обхода, поэтому результат вывода тоже разный.

❗️Для однопоточных коллекций между for и forEach нет никакой разницы, в обоих случаях используется итератор.
  • 👍 2
More from @java_fillthegaps
  1. Apr 30, 2026Get Your Hands Dirty on Clean Architecture: отзыв на книгу Когда я подняла тему чистой арх…
  2. Apr 27, 2026​Clean Architecture: отзыв на книгу Наконец-то дочитала книгу Clean Architecture Роберта М…
  3. Mar 4, 2026Как переиспользовать контекст в интеграционных тестах Сегодня расскажу базовый минимум для…
  4. Mar 4, 2026Post #660
  5. Mar 4, 2026Тестовый контекст поднимается 2 минуты. У нас 4 класса с интеграционными тестами, их конфи…
  6. Feb 25, 2026Чистая архитектура. Главы 3-5 Продолжаем спидран по Clean Architecture Роберта Мартина. ⭐️…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →