0Pricing
Java Academy · Урок

ListIterator и двунаправленный обход

Используйте ListIterator для обхода списков вперёд и назад и изменения элементов во время перебора.

«ListIterator и двунаправленный обход» — бесплатный урок Java Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

ListIterator

ListIterator<T> расширяет Iterator<T>, добавляя обратный обход, позиционирование по индексу и возможность добавлять и заменять элементы во время перебора.

API ListIterator

Дополнительные методы по сравнению с итератором: hasPrevious(), previous(), nextIndex(), previousIndex(), set(), add().

import java.util.*;

List<String> list = new ArrayList<>(List.of("A", "B", "C", "D"));
ListIterator<String> lit = list.listIterator();

// Forward traversal
while (lit.hasNext()) {
    System.out.print(lit.nextIndex() + ":" + lit.next() + " ");
}
// 0:A 1:B 2:C 3:D

System.out.println();

// Backward traversal
while (lit.hasPrevious()) {
    System.out.print(lit.previousIndex() + ":" + lit.previous() + " ");
}
// 3:D 2:C 1:B 0:A

Начало с определённой позиции

Создайте ListIterator, начиная с определённого индекса, с помощью listIterator(index).

List<Integer> nums = new ArrayList<>(List.of(10, 20, 30, 40, 50));

// Start at index 2 (30)
ListIterator<Integer> lit = nums.listIterator(2);

System.out.println(lit.next());     // 30
System.out.println(lit.next());     // 40
System.out.println(lit.previous()); // 40
System.out.println(lit.previous()); // 30
System.out.println(lit.previous()); // 20

set() при итерации

set() заменяет последний элемент, возвращённый next() или previous(). Это эффективно: O(1) для LinkedList и O(1) для ArrayList.

List<String> words = new ArrayList<>(List.of("hello", "world", "java"));
ListIterator<String> lit = words.listIterator();

while (lit.hasNext()) {
    String word = lit.next();
    lit.set(word.toUpperCase()); // replace each with uppercase
}

System.out.println(words); // [HELLO, WORLD, JAVA]

add() при итерации

add(element) вставляет элемент перед следующей позицией. Добавленный элемент не возвращается при последующем вызове next(): он уже находится «позади» курсора.

List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3));
ListIterator<Integer> lit = numbers.listIterator();

while (lit.hasNext()) {
    int n = lit.next();
    lit.add(n * 10); // insert n*10 after each element
}

System.out.println(numbers); // [1, 10, 2, 20, 3, 30]

Разворот списка на месте

С помощью ListIterator разверните список за O(n), не выделяя память под новый список.

static <T> void reverse(List<T> list) {
    ListIterator<T> front = list.listIterator(0);
    ListIterator<T> back  = list.listIterator(list.size());

    for (int i = 0, n = list.size() / 2; i < n; i++) {
        T frontVal = front.next();
        T backVal  = back.previous();
        front.set(backVal);
        back.set(frontVal);
    }
}

List<String> data = new ArrayList<>(List.of("a","b","c","d","e"));
reverse(data);
System.out.println(data); // [e, d, c, b, a]

nextIndex и previousIndex

nextIndex() возвращает индекс элемента, который был бы возвращён методом next(). previousIndex() возвращает индекс элемента, который был бы возвращён методом previous().

List<String> list = List.of("X", "Y", "Z");
ListIterator<String> lit = list.listIterator();

System.out.println(lit.nextIndex());     // 0
System.out.println(lit.previousIndex()); // -1 (before start)

lit.next(); // consume X
System.out.println(lit.nextIndex());     // 1
System.out.println(lit.previousIndex()); // 0

Производительность ListIterator для LinkedList

ListIterator в LinkedList выполняет next/previous за O(1), поскольку обход следует по указателям. Вызов get(i) для LinkedList выполняется за O(n), поэтому для эффективного обхода используйте ListIterator.

import java.util.*;

LinkedList<Integer> list = new LinkedList<>();
for (int i = 0; i < 5; i++) list.add(i * 10);

// Efficient: O(n) total for traversal via ListIterator
ListIterator<Integer> lit = list.listIterator();
while (lit.hasNext()) {
    int val = lit.next();
    if (val == 20) lit.set(99); // O(1) update
}
System.out.println(list); // [0, 10, 99, 30, 40]

Проверка палиндрома с помощью ListIterator

Элегантный пример двунаправленного обхода: проверка того, является ли список палиндромом.

static <T> boolean isPalindrome(List<T> list) {
    ListIterator<T> front = list.listIterator(0);
    ListIterator<T> back  = list.listIterator(list.size());

    for (int i = 0, n = list.size() / 2; i < n; i++) {
        if (!front.next().equals(back.previous())) return false;
    }
    return true;
}

System.out.println(isPalindrome(List.of(1, 2, 3, 2, 1))); // true
System.out.println(isPalindrome(List.of(1, 2, 3, 4)));    // false

Сравнение Iterator и ListIterator

Основные различия:

  • Iterator: только вперёд, hasNext/next/remove
  • ListIterator: в обоих направлениях, hasPrevious/previous, nextIndex/previousIndex, set/add
  • ListIterator работает только со списками, но не с Set и Queue

Отмена и повтор с помощью ListIterator

Текстовый редактор на основе курсора, использующий ListIterator для эффективной навигации по символам при отмене и повторе действий.

import java.util.*;

class TextEditor {
    private final LinkedList<Character> chars = new LinkedList<>();
    private ListIterator<Character> cursor;

    TextEditor() { cursor = chars.listIterator(); }

    void type(char c) { cursor.add(c); } // insert before cursor

    void moveCursorLeft()  { if (cursor.hasPrevious()) cursor.previous(); }
    void moveCursorRight() { if (cursor.hasNext()) cursor.next(); }

    void backspace() {
        if (cursor.hasPrevious()) { cursor.previous(); cursor.remove(); }
    }

    String text() {
        StringBuilder sb = new StringBuilder();
        chars.forEach(sb::append);
        return sb.toString();
    }
}

TextEditor ed = new TextEditor();
ed.type('H'); ed.type('e'); ed.type('l'); ed.type('o');
ed.moveCursorLeft(); ed.moveCursorLeft();
ed.type('l');
System.out.println(ed.text()); // Hello

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

Используйте ListIterator, когда требуется:

  • обход списка в обратном направлении;
  • замена элементов во время обхода с помощью set();
  • вставка элементов во время обхода с помощью add();
  • отслеживание позиции через nextIndex()/previousIndex().

Быстрая проверка

Что делает ListIterator.set()?

Итоги: ListIterator и двунаправленный обход

Главные выводы:

  • ListIterator расширяет Iterator методами hasPrevious(), previous(), set(), add()
  • Создавайте ListIterator в определённой позиции: listIterator(index)
  • set() заменяет последний элемент, возвращённый next() или previous()
  • add() вставляет элемент перед текущей позицией курсора
  • nextIndex() и previousIndex() позволяют узнать позицию
  • Используйте ListIterator для LinkedList, чтобы выполнять обход и изменения за O(1)

Часто задаваемые вопросы

Урок «ListIterator и двунаправленный обход» бесплатный?

Да — полный текст урока «ListIterator и двунаправленный обход» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «ListIterator и двунаправленный обход»?

Используйте ListIterator для обхода списков вперёд и назад и изменения элементов во время перебора. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «ListIterator и двунаправленный обход»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Контракты Iterable и Iterator
  2. Реализация собственного итератора
  3. ListIterator и двунаправленный обход
  4. Отказоустойчивые и безопасные итераторы
← Назад к Java Academy