Enhanced for loop (for-each)

Enhanced for loop (также называется for-each) — это сокращённый синтаксис цикла, появившийся в Java 5. Он позволяет обойти массив или любую коллекцию, реализующую Iterable, без явного индекса и итератора.

Классический for (int i = 0; i < arr.length; i++) хорош, когда нужен индекс. Но в 80% случаев индекс не нужен — нужны сами элементы. Тут и помогает for-each: он читается почти как фраза «для каждого элемента в коллекции — сделай …».

Минимальная версия: Java 5.

Синтаксис

for (ТипЭлемента переменная : массивИлиИтерабельное) {
    // тело цикла
}

Под капотом for-each:

  • для массивов — обычный цикл по индексу;

  • для Iterable — цикл с Iterator, hasNext(), next().

Зачем нужно

  • Меньше шума: нет i = 0, i++, arr[i].

  • Невозможно случайно выйти за пределы.

  • Универсально работает с массивами, List, Set, Map.entrySet() и любыми Iterable.

Пример 1. Массив примитивов

public class ForEachArray {
    public static void main(String[] args) {
        int[] linePins = {14, 15, 16}; // A0, A1, A2

        int sum = 0;
        for (int pin : linePins) {
            System.out.println("Pin A" + (pin - 14));
            sum += pin;
        }
        System.out.println("Сумма номеров: " + sum);
    }
}

Output:

Pin A0
Pin A1
Pin A2
Сумма номеров: 45

Пример 2. List строк

import java.util.List;

public class ForEachList {
    public static void main(String[] args) {
        List<String> components = List.of(
            "L298N", "RCWL-9610A", "SG92R", "HM-10", "VS1838B"
        );

        int index = 0;
        for (String c : components) {
            System.out.println(index + ": " + c);
            index++;
        }
    }
}

Output:

0: L298N
1: RCWL-9610A
2: SG92R
3: HM-10
4: VS1838B

Пример 3. Обход Map через entrySet

import java.util.LinkedHashMap;
import java.util.Map;

public class ForEachMap {
    public static void main(String[] args) {
        Map<String, Integer> pins = new LinkedHashMap<>();
        pins.put("trig", 3);
        pins.put("echo", 7);
        pins.put("servo", 9);

        for (Map.Entry<String, Integer> e : pins.entrySet()) {
            System.out.println(e.getKey() + " -> D" + e.getValue());
        }
    }
}

Output:

trig -> D3
echo -> D7
servo -> D9

Пример 4. Вложенные for-each (двумерный массив)

public class ForEachNested {
    public static void main(String[] args) {
        int[][] matrix = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };

        for (int[] row : matrix) {
            for (int cell : row) {
                System.out.print(cell + " ");
            }
            System.out.println();
        }
    }
}

Output:

1 2 3
4 5 6
7 8 9

Пример 5. Свой Iterable

import java.util.Iterator;

public class ForEachIterable {
    // Простой Iterable, отдающий числа от 1 до n
    static class Range implements Iterable<Integer> {
        private final int n;
        Range(int n) { this.n = n; }
        public Iterator<Integer> iterator() {
            return new Iterator<>() {
                int i = 1;
                public boolean hasNext() { return i <= n; }
                public Integer next() { return i++; }
            };
        }
    }

    public static void main(String[] args) {
        for (int x : new Range(5)) {
            System.out.print(x + " ");
        }
        System.out.println();
    }
}

Output:

1 2 3 4 5

Пример 6. Что НЕЛЬЗЯ делать в for-each

import java.util.ArrayList;
import java.util.List;

public class ForEachLimits {
    public static void main(String[] args) {
        // 1) Нет доступа к индексу
        List<String> kits = new ArrayList<>(List.of("A", "B", "C"));
        int i = 0;
        for (String k : kits) {
            System.out.println("[" + i + "] = " + k);
            i++;
        }

        // 2) Нельзя изменять коллекцию во время обхода — ConcurrentModificationException
        try {
            for (String k : kits) {
                if (k.equals("B")) kits.remove(k); // НЕЛЬЗЯ
            }
        } catch (Exception e) {
            System.out.println("Ошибка: " + e.getClass().getSimpleName());
        }

        // 3) Изменение переменной цикла не влияет на массив
        int[] arr = {10, 20, 30};
        for (int x : arr) {
            x = x * 2;  // x — копия, массив не меняется
        }
        System.out.print("Массив после: ");
        for (int x : arr) System.out.print(x + " ");
        System.out.println();
    }
}

Output:

[0] = A
[1] = B
[2] = C
Ошибка: ConcurrentModificationException
Массив после: 10 20 30

Подводные камни

Предупреждение

  • Нет доступа к индексу элемента — придётся вести счётчик вручную.

  • Нельзя модифицировать коллекцию (add/remove) во время обхода: получите ConcurrentModificationException. Используйте Iterator.remove() или собирайте элементы для удаления отдельно.

  • Переменная цикла — это копия ссылки (или примитива). Присваивание ей не меняет массив.

  • Для параллельного обхода двух коллекций for-each не подходит — нужен индекс или Iterator.

  • for-each не работает с null — будет NullPointerException. Проверяйте коллекцию заранее.

Совет

Если индекс действительно нужен, используйте классический for или IntStream.range(0, list.size()). Для функционального стиля рассмотрите stream().forEach(...).

См. также

Примечание

Материал основан на официальной документации Oracle Java SE (docs.oracle.com/en/java/javase), распространяемой под лицензией Oracle Free Documentation License. Тексты и примеры написаны заново для AlashEd Wiki.