tgoop.com/java_fillthegaps/296
Last Update:
Разница между for и forEach
довольно проста. for использует для обхода итератор, а forEach - траверс.
В этом посте разберём разницу между ними. Цель — чисто познавательная: как компьютер саенс проявляется в языке программирования.
Сразу скажу: всё это имеет смысл только для обхода многопоточных структур данных. Если мы попробуем изменить ArrayList, HashMap и т.д во время обхода, то получим ConcurrentModificationException.
Начнём издалека и посмотрим на обход более сложной сущности — графа. Граф состоит из вершин и рёбер, в нём могут быть циклы.
Обойти все вершины графа по одному разу (найти гамильтонов путь) — непростая задача. Алгоритм ведёт учёт посещённых вершин и перебирает множество вариантов. В computer science обход структуры данных называется traverse.
🔸Самое важное: следующий элемент вычисляется только, когда понадобится.
Вернёмся в мир java. В стандартных структурах царят списки, множества, очереди и деревья. Циклов здесь нет, следующий элемент при обходе однозначен, а его отсутствие означает конец работы. Поэтому обход выглядит так:
Iterator it=list.iterator();Метод next возвращает текущий элемент и сдвигает указатель на следующий. Метод hasNext проверяет, ссылается ли этот указатель куда-нибудь. Этот паттерн повторяется снова и снова и называется Iteration.
while(it.hasNext())
int result = it.next();
Итератор лежит в основе синтаксиса for (T e: collection)
🔸Самое важное: указатель на следующий элемент вычисляется заранее.
Возможный минус: если мы удалили или поменяли элемент, который должен вывестись следующим, то итератор не подхватит изменений и выведет старое значение.
Что по задаче:
Обход через for использует итератор. Указатель на следующий элемент вычисляется заранее. Более подходящий новый элемент не отображается, и выводится 2 элемента.
Метод forEach использует траверс и вычисляет следующий элемент только когда он запрашивается. Поэтому новый ключ подхватывается, и в консоль попадают 3 элемента.
❓Почему нельзя использовать траверс по умолчанию?
— Итератор проще и работает быстрее, а условия для пропуска элемента при обходе встречаются редко.
❓Зачем нужно несколько вариантов?
— ConcurrentHashMap может перестраиваться во время обхода. Чтобы во время перестройки не выводить дубликаты, используется траверс со сложной логикой.
Итого: при выводе элементов ConcurrentHashMap через for и forEach используются разные алгоритмы обхода, поэтому результат вывода тоже разный.
❗️Для однопоточных коллекций между for и forEach нет никакой разницы, в обоих случаях используется итератор.
BY Java: fill the gaps
Share with your friend now:
tgoop.com/java_fillthegaps/296