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()); // 20set() при итерации
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 — локальная установка не требуется.
Все уроки этого курса
- Контракты Iterable и Iterator
- Реализация собственного итератора
- ListIterator и двунаправленный обход
- Отказоустойчивые и безопасные итераторы