0Pricing
Java Academy · レッスン

ListIterator と双方向走査

ListIterator でリストを前後に走査し、反復中に要素を変更します。

「ListIterator と双方向走査」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

ListIterator

ListIterator<T>はIterator<T>を拡張し、逆方向の走査、インデックスによる位置指定、反復処理中の要素の追加と置換を可能にします。

ListIterator API

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

指定した位置から開始

listIterator(index) を使用すると、指定したインデックスから ListIterator を作成できます。

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() が最後に返した要素を置き換えます。効率もよく、LinkedList では O(1)、ArrayList でも O(1) です。

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

LinkedList の ListIterator のパフォーマンス

LinkedList の ListIterator では、ポインターをたどるだけなので next/previous の処理が O(1) になります。一方、LinkedList での get(i) は 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 は List 専用(Set や Queue では使用不可)

ListIterator による Undo/Redo

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 は hasPrevious()、previous()、set()、add() によって Iterator を拡張します
  • 特定の位置から ListIterator を作成するには listIterator(index) を使用します
  • set() は next() または previous() が最後に返した要素を置き換えます
  • add() は現在のカーソル位置の前に要素を挿入します
  • nextIndex() と previousIndex() で現在位置を把握できます
  • O(1) の走査と変更には LinkedList の ListIterator を使用してください

よくある質問

「ListIterator と双方向走査」レッスンは無料ですか?

はい。「ListIterator と双方向走査」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「ListIterator と双方向走査」で何を学びますか?

ListIterator でリストを前後に走査し、反復中に要素を変更します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「ListIterator と双方向走査」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Iterable と Iterator の契約
  2. カスタム Iterator の実装
  3. ListIterator と双方向走査
  4. Fail-Fast と Fail-Safe の Iterator
← Java Academyに戻る