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()); // 0LinkedList の 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))); // falseIterator と 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()); // HelloListIterator を使用する場面
次の処理が必要な場合は 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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Iterable と Iterator の契約
- カスタム Iterator の実装
- ListIterator と双方向走査
- Fail-Fast と Fail-Safe の Iterator