← Назад к вопросам

Как итерироваться по коллекциям

1.0 Junior🔥 121 комментариев
#Коллекции

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы итерации по коллекциям в Java

Выбор правильного способа итерации влияет на производительность и читаемость. За 10+ лет я видел все варианты, вот моя рекомендация.

1. for-each (Enhanced for loop) — РЕКОМЕНДУЕМЫЙ

Самый читаемый и безопасный способ.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

for (String name : names) {
  System.out.println(name);
}

Как это работает (под капотом):

  • Для Iterable → использует Iterator
  • Для массива → автоматически раскрывается в индексный цикл

Преимущества: ✅ Читаемо ✅ Безопасно (нет ConcurrentModificationException при правильном использовании) ✅ Работает с любыми коллекциями ✅ Компилятор оптимизирует для массивов

2. Iterator — для удаления во время итерации

ТОЛЬКО способ, при котором безопасно удалять элементы.

List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));

Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
  String name = iterator.next();
  if (name.startsWith("B")) {
    iterator.remove(); // Безопасно!
  }
}

Опасно (ConcurrentModificationException):

for (String name : names) {
  if (name.startsWith("B")) {
    names.remove(name); // ОШИБКА! Exception
  }
}

Когда использовать:

  • Нужно удалить элементы во время итерации
  • Работа со специализированными коллекциями (TreeSet, LinkedList)
  • Нужен контроль над итерацией (remove, hasNext)

3. forEach (Functional) — современный подход

Java 8+, функциональный стиль.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Lambda
names.forEach(name -> System.out.println(name));

// Method reference
names.forEach(System.out::println);

Когда использовать:

  • Простые операции
  • Функциональный стиль предпочтителен
  • Параллельная обработка (parallelStream)

Когда НЕ использовать:

  • Нужно удалить элемент (нет доступа к Iterator)
  • Нужен break/continue
  • Нужен индекс элемента

4. Stream API — для сложной обработки

Самый мощный, но не всегда необходимый.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// Фильтр + преобразование
numbers.stream()
  .filter(n -> n > 2)
  .map(n -> n * 2)
  .forEach(System.out::println); // 6, 8, 10

// С параллелизмом
numbers.parallelStream()
  .filter(n -> n > 2)
  .map(n -> expensiveOperation(n))
  .collect(Collectors.toList());

Когда использовать:

  • Нужны преобразования (map, filter, reduce)
  • Большой набор данных с parallelStream
  • Функциональный стиль

Когда НЕ использовать:

  • Простая итерация (оверинжиниринг)
  • Нужен контроль и break/continue
  • Проблемы с производительностью (stream имеет overhead)

5. Индексный цикл — для массивов и List

Самый быстрый для RandomAccess коллекций.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

for (int i = 0; i < names.size(); i++) {
  System.out.println(names.get(i));
}

Когда использовать:

  • Нужен индекс элемента
  • ArrayList (RandomAccess)
  • Критична производительность на больших массивах

Когда НЕ использовать:

  • LinkedList (get(i) = O(n), общая сложность O(n²))
  • Нельзя удалять элементы
  • Нужна простота (используй for-each)

Опасность:

LinkedList<String> names = new LinkedList<>(Arrays.asList("Alice", "Bob", "Charlie"));

// O(n²) - ОЧЕНЬ МЕДЛЕННО!
for (int i = 0; i < names.size(); i++) {
  System.out.println(names.get(i)); // Каждый get() проходит от начала
}

// O(n) - быстро
for (String name : names) {
  System.out.println(name); // Использует Iterator
}

6. While с Iterator

Альтернатива Iterator, нужна явная проверка.

Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
  System.out.println(iterator.next());
}

Сравнительная таблица

╔═══════════════════════╦════════════╦═════════════╦════════════════╗
║ Метод                 ║ Скорость   ║ Безопасность║ Гибкость       ║
╠═══════════════════════╬════════════╬═════════════╬════════════════╣
║ for-each              ║ Good       ║ Safe        ║ Limited        ║
║ Iterator              ║ Good       ║ Safe (remove)║ Excellent      ║
║ forEach (lambda)      ║ Good       ║ Safe        ║ Good           ║
║ Stream API            ║ Medium     ║ Safe        ║ Excellent      ║
║ Индексный цикл       ║ Excellent  ║ Risky       ║ Good (+ index) ║
║ While+Iterator        ║ Good       ║ Safe        ║ Excellent      ║
╚═══════════════════════╩════════════╩═════════════╩════════════════╝

Практический пример: Рекомендуемая иерархия

// 1️⃣ Простая итерация -> for-each
for (User user : users) {
  System.out.println(user.getName());
}

// 2️⃣ Нужен индекс -> индексный цикл (для ArrayList)
for (int i = 0; i < users.size(); i++) {
  System.out.println(i + ": " + users.get(i));
}

// 3️⃣ Удаление -> Iterator
Iterator<User> it = users.iterator();
while (it.hasNext()) {
  User user = it.next();
  if (user.isInactive()) it.remove();
}

// 4️⃣ Преобразование -> Stream
List<String> names = users.stream()
  .filter(u -> u.isActive())
  .map(User::getName)
  .collect(Collectors.toList());

// 5️⃣ Большие данные + параллелизм -> parallelStream
users.parallelStream()
  .filter(u -> u.isActive())
  .map(this::expensiveProcessing)
  .forEach(System.out::println);

Специальные коллекции

Map:

Map<String, Integer> scores = new HashMap<>();

// Способ 1: entrySet (САМЫЙ БЫСТРЫЙ)
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
  System.out.println(entry.getKey() + ": " + entry.getValue());
}

// Способ 2: forEach
scores.forEach((key, value) -> System.out.println(key + ": " + value));

// Способ 3: keySet/values (МЕДЛЕННЕЕ)
for (String key : scores.keySet()) {
  System.out.println(key + ": " + scores.get(key)); // Лишний get()
}

Итоговые рекомендации

По умолчанию: for-each ✅ Удаление: Iterator ✅ С индексом: индексный цикл или IntStream ✅ Преобразования: Stream API ✅ Параллельная обработка: parallelStream ❌ Избегай: индексный цикл на LinkedList ❌ Избегай: удаление через remove() в for-each

Выбирай самый простой способ, который решает задачу. Оптимизируй только если профилер показывает проблему.