Цикл for-each (расширенный for) в Java
Обход массивов и коллекций в Java с помощью расширенного цикла for-each для более простой и безопасной итерации.
В Java 5 был введён расширенный цикл for — чаще называемый циклом for-each — для обхода каждого элемента массива или коллекции без ручного управления индексом. Когда индекс не нужен, это почти всегда самый чистый выбор.
Синтаксис
for (Type element : iterable) {
// body — runs once per element
}Читается как: для каждого element в iterable. Двоеточие обязательно; это не то же самое, что точки с запятой в обычном цикле for.
int[] nums = {10, 20, 30, 40};
for (int n : nums) {
System.out.println(n);
}Выводит 10 20 30 40. Никакого счётчика, никакого nums.length, никакого nums[i].
Работает с массивами и любым Iterable
Цикл for-each принимает:
- Любой массив.
- Всё, что реализует
java.lang.Iterable<T>— то есть каждуюCollection(такую какList,SetиQueue), а также многие другие типы JDK и сторонних библиотек.
import java.util.List;
List<String> names = List.of("alice", "bob", "carol");
for (String name : names) {
System.out.println(name);
}
String text = "abc";
for (char c : text.toCharArray()) {
System.out.println(c);
}Обратите внимание, что String сам по себе не является итерируемым, но text.toCharArray() возвращает массив, который им является.
Map также не является Iterable напрямую — вместо этого нужно итерировать одно из его представлений: map.keySet(), map.values() или map.entrySet(). Каждое из них возвращает Set или Collection, с которыми цикл for-each справляется.
Переменная элемента является копией
Переменная в заголовке цикла получает значение каждого элемента. Для примитивов её изменение не влияет на массив:
int[] nums = {1, 2, 3};
for (int n : nums) {
n *= 10; // doesn't touch nums
}
System.out.println(nums[0]); // still 1Для объектов переменная содержит ссылку, поэтому вызов методов, изменяющих объект, повлияет на оригинал. Но переприсвоение переменной (name = "...";) изменяет только локальную копию.
Если вам нужно изменить сами элементы, используйте цикл с индексом:
for (int i = 0; i < nums.length; i++) {
nums[i] *= 10;
}Когда нельзя использовать for-each
Цикл for-each скрывает индекс. Это его суть — но это также означает, что его нельзя использовать, когда нужно:
- Индекс (
"item " + i + ": " + value) - Удалять элементы во время итерации (используйте явный
Iteratorи вызывайтеiterator.remove()) - Синхронно обходить две коллекции (используйте индексы)
- Итерировать в обратном порядке или пропускать элементы (используйте индексированный
for)
Не изменяйте коллекцию во время итерации
Цикл for-each по коллекции использует Iterator внутри. Если вы изменяете структуру коллекции во время итерации (добавляете или удаляете элементы), итератор обнаруживает несоответствие на следующем шаге и выбрасывает ConcurrentModificationException:
List<String> names = new ArrayList<>(List.of("alice", "bob"));
for (String n : names) {
names.remove(n); // ConcurrentModificationException
}Для безопасного удаления управляйте циклом с помощью явного Iterator и вызывайте его remove() — это синхронизирует итератор и список:
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String n = it.next();
if (n.startsWith("a")) {
it.remove(); // safe
}
}В Java 8+ цикл часто можно полностью пропустить: names.removeIf(n -> n.startsWith("a"));.
Чтение или замена элемента (list.set(i, x)) во время цикла for-each допустимы. Исключение вызывают только структурные изменения — add и remove самой коллекции.
Рабочий пример
Что дальше
Когда нужно досрочно выйти из цикла — или пропустить остаток текущей итерации — используйте break и continue.