W3docs

Цикл 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 самой коллекции.

Рабочий пример

java— editable, runs on the server

Что дальше

Когда нужно досрочно выйти из цикла — или пропустить остаток текущей итерации — используйте break и continue.

Практика

Практика
В цикле for-each по int[], изменение переменной цикла внутри тела...
В цикле for-each по int[], изменение переменной цикла внутри тела...
Was this page helpful?