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

Какой интерфейс отвечает за итерируемость коллекций?

1.0 Junior🔥 191 комментариев
#Коллекции#Основы Java

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

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

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

Итерируемость коллекций: интерфейсы и механизмы

В Java за возможность итерировать коллекции отвечает иерархия интерфейсов, каждый из которых имеет собственную роль и ответственность. Это ключевой момент для понимания архитектуры Collections Framework.

Главный интерфейс: Iterable<T>

Iterable — это корневой интерфейс, отвечающий за итерируемость объекта.

public interface Iterable<T> {
    Iterator<T> iterator();  // Основной метод
    
    default void forEach(Consumer<? super T> action) {
        for (T t : this) {
            action.accept(t);
        }
    }
    
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(
            iterator(), 0);
    }
}

Ключевой момент: если класс реализует Iterable, то его объект можно итерировать в for-each loop (enhanced for):

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

// Возможно благодаря реализации Iterable
for (String name : names) {  // ← за этим скрывается iterator()
    System.out.println(name);
}

Iterator<T> — интерфейс для перемещения

Iterator отвечает за последовательный обход элементов коллекции.

public interface Iterator<E> {
    boolean hasNext();   // Есть ещё элементы?
    E next();           // Получить следующий
    void remove();      // Удалить текущий (опционально)
    
    default void forEachRemaining(Consumer<? super E> action) {
        while (hasNext()) {
            action.accept(next());
        }
    }
}

Пример использования:

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

// Явное получение Iterator
Iterator<Integer> iterator = numbers.iterator();

while (iterator.hasNext()) {
    Integer num = iterator.next();
    System.out.println(num);
    
    if (num == 3) {
        iterator.remove();  // Безопасное удаление во время итерации
    }
}

// После цикла numbers содержит: [1, 2, 4, 5]

Почему Iterator важен:

// ❌ Опасно удалять из коллекции во время for-each
for (String name : list) {
    if (name.equals("Bob")) {
        list.remove(name);  // ← ConcurrentModificationException!
    }
}

// ✅ Iterator позволяет безопасно удалять
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String name = it.next();
    if (name.equals("Bob")) {
        it.remove();  // ← Безопасно
    }
}

Иерархия интерфейсов

Iterable<T>
    ↓
  (вызывает)
    ↓
Iterator<T>  ←  Основной интерфейс итерации
    ↓
  (используется в)
    ↓
Listen.forEach()  ←  Удобный способ обхода

Связь между Iterable, Iterator и Collection

// Collection иерархия
public interface Collection<E> extends Iterable<E> {
    // Наследует iterator()
    Iterator<E> iterator();
    // ...
}

public interface List<E> extends Collection<E> {
    // Наследует iterator()
    // Добавляет ListIterator для двусторонней итерации
    ListIterator<E> listIterator();
    // ...
}

ListIterator — расширенная итерация

Для List коллекций есть ListIterator — более мощный Iterator с дополнительными методами:

public interface ListIterator<E> extends Iterator<E> {
    // Из Iterator
    boolean hasNext();
    E next();
    void remove();
    
    // Дополнительные методы
    boolean hasPrevious();      // Есть элемент ДО текущего?
    E previous();               // Получить предыдущий
    int nextIndex();            // Индекс следующего
    int previousIndex();        // Индекс предыдущего
    void set(E e);             // Заменить текущий
    void add(E e);             // Добавить перед текущим
}

Пример использования ListIterator:

List<String> list = new ArrayList<>(Arrays.asList(
    "Первый", "Второй", "Третий", "Четвёртый"
));

// Двусторонняя итерация
ListIterator<String> it = list.listIterator();

while (it.hasNext()) {
    it.next();
}

// Теперь итерируем назад
while (it.hasPrevious()) {
    System.out.println("Назад: " + it.previous());
}

// Пример: замена элемента
ListIterator<String> it2 = list.listIterator();
while (it2.hasNext()) {
    String element = it2.next();
    if (element.equals("Второй")) {
        it2.set("ИЗМЕНЁН");  // Заменяет текущий элемент
    }
}

Spliterator — современная итерация

Запущен в Java 8 для поддержки параллельных потоков (Streams API):

public interface Spliterator<T> {
    boolean tryAdvance(Consumer<? super T> action);  // Обработать один элемент
    Spliterator<T> trySplit();  // Разделить на две части для параллелизма
    long estimateSize();        // Оценённый размер
    int characteristics();      // ORDERED, DISTINCT, SORTED, etc.
}

Использование в Stream API:

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

// Spliterator используется автоматически при работе со Stream
int sum = numbers.stream()  // ← stream() использует spliterator()
    .parallel()            // ← может разделиться на несколько потоков
    .filter(n -> n > 2)
    .mapToInt(Integer::intValue)
    .sum();

System.out.println(sum);  // 12 (3 + 4 + 5)

Практические примеры

1. Создание собственного итерируемого класса

public class Range implements Iterable<Integer> {
    private int start;
    private int end;
    
    public Range(int start, int end) {
        this.start = start;
        this.end = end;
    }
    
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            private int current = start;
            
            @Override
            public boolean hasNext() {
                return current < end;
            }
            
            @Override
            public Integer next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
                return current++;
            }
        };
    }
}

// Использование
for (int num : new Range(1, 5)) {
    System.out.println(num);  // 1, 2, 3, 4
}

2. Потокобезопасная итерация

List<String> list = Collections.synchronizedList(new ArrayList<>());

// Iterator гарантирует потокобезопасность при использовании
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

3. ConcurrentModificationException — что это?

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

// ❌ Ошибка
try {
    for (int num : list) {
        if (num == 3) {
            list.remove((Integer) num);  // ← изменяем коллекцию
        }
    }
} catch (ConcurrentModificationException e) {
    System.out.println("Ошибка итерации!");
}

// ✅ Правильно
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
    int num = it.next();
    if (num == 3) {
        it.remove();  // ← безопасное удаление через Iterator
    }
}

Таблица интерфейсов

ИнтерфейсМетодНазначениеВерсия
Iterable<T>iterator()Основа для итерацииJava 5
Iterator<T>hasNext(), next()Последовательный обходJava 1.2
ListIterator<T>previous(), hasPrevious()Двусторонняя итерацияJava 1.2
Spliterator<T>tryAdvance(), trySplit()Параллельная итерацияJava 8

Заключение

За итерируемость отвечают:

  1. Iterable<T> — интерфейс, который нужно реализовать классу для возможности итерации
  2. Iterator<T> — непосредственно механизм перемещения по элементам
  3. ListIterator<T> — расширение для двусторонней итерации
  4. Spliterator<T> — современный подход для параллельной обработки

Понимание этой иерархии критично для:

  • Правильного использования Collections Framework
  • Создания собственных итерируемых классов
  • Избежания ConcurrentModificationException
  • Написания параллельного кода со Stream API