0Pricing
Java Academy · Lekcja

ListIterator i przechodzenie dwukierunkowe

Używaj ListIterator do przechodzenia po listach w przód i w tył oraz modyfikowania elementów podczas iteracji.

ListIterator i przechodzenie dwukierunkowe to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

ListIterator

ListIterator<T> rozszerza Iterator<T> o przechodzenie wstecz, pozycjonowanie na podstawie indeksu oraz możliwość dodawania i zastępowania elementów podczas iterowania.

API ListIterator

Dodatkowe metody poza Iterator: 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

Rozpoczynanie od określonej pozycji

Utworzenie obiektu ListIterator rozpoczynającego działanie od określonego indeksu za pomocą 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() podczas iteracji

set() zastępuje ostatni element zwrócony przez next() lub previous(). Jest to wydajne — O(1) dla LinkedList i O(1) dla 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() podczas iteracji

add(element) wstawia element przed następną pozycją. Dodany element nie zostanie zwrócony przez kolejne wywołanie next() — znajduje się już „za” kursorem.

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]

Odwracanie listy w miejscu

Użycie ListIterator do odwrócenia listy w czasie O(n), bez przydzielania nowej listy.

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 i previousIndex

nextIndex() zwraca indeks elementu, który zostałby zwrócony przez next(). previousIndex() zwraca indeks elementu, który zostałby zwrócony przez 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

Wydajność ListIterator dla LinkedList

ListIterator dla LinkedList zapewnia O(1) dla next/previous, ponieważ przechodzenie polega na podążaniu za wskaźnikami. get(i) dla LinkedList ma złożoność O(n) — do wydajnego przechodzenia należy używać 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]

Sprawdzanie palindromu za pomocą ListIterator

Elegancki przykład przechodzenia dwukierunkowego: sprawdzanie, czy lista jest palindromem.

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

Porównanie Iterator i ListIterator

Najważniejsze różnice:

  • Iterator: tylko do przodu, hasNext/next/remove
  • ListIterator: dwukierunkowy, hasPrevious/previous, nextIndex/previousIndex, set/add
  • ListIterator działa wyłącznie z listami (nie z Set ani Queue)

Cofanie i ponawianie zmian za pomocą ListIterator

Edytor tekstu oparty na kursorze, wykorzystujący ListIterator do wydajnego poruszania się po znakach podczas cofania i ponawiania zmian.

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

Kiedy używać ListIterator

ListIterator należy używać, gdy potrzebne są:

  • przechodzenie po liście wstecz
  • zastępowanie elementów podczas przechodzenia za pomocą set()
  • wstawianie elementów podczas przechodzenia za pomocą add()
  • śledzenie pozycji za pomocą nextIndex()/previousIndex()

Szybkie sprawdzenie

Co robi ListIterator.set()?

Podsumowanie: ListIterator i iteracja dwukierunkowa

Najważniejsze informacje:

  • ListIterator rozszerza Iterator o hasPrevious(), previous(), set() i add()
  • ListIterator można utworzyć w określonej pozycji za pomocą listIterator(index)
  • set() zastępuje ostatni element zwrócony przez next() lub previous()
  • add() wstawia element przed bieżącą pozycją kursora
  • nextIndex() i previousIndex() dostarczają informacji o pozycji
  • ListIterator dla LinkedList należy stosować do przechodzenia i modyfikowania elementów w czasie O(1)

Często zadawane pytania

Czy lekcja „ListIterator i przechodzenie dwukierunkowe” jest bezpłatna?

Tak — pełny tekst „ListIterator i przechodzenie dwukierunkowe” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „ListIterator i przechodzenie dwukierunkowe”?

Używaj ListIterator do przechodzenia po listach w przód i w tył oraz modyfikowania elementów podczas iteracji. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „ListIterator i przechodzenie dwukierunkowe”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Kontrakty Iterable i Iterator
  2. Implementowanie własnego iteratora
  3. ListIterator i przechodzenie dwukierunkowe
  4. Iteratory fail-fast a fail-safe
← Powrót do Java Academy