การสร้าง Iterator แบบกำหนดเอง
สร้างคลาส iterator แบบกำหนดเองสำหรับรายการเชื่อมโยงหรือโครงสร้างช่วงค่าอย่างง่าย
การสร้าง Iterator แบบกำหนดเอง เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
Iterator แบบกำหนดเอง
การสร้างตัววนซ้ำแบบกำหนดเองช่วยให้คุณควบคุมวิธีท่องผ่านโครงสร้างข้อมูลได้อย่างเต็มที่ บทเรียนนี้อธิบายการนำตัววนซ้ำของรายการเชื่อมโยงไปใช้ทีละขั้นตอน
คลาส Node
ขั้นแรก ให้กำหนดโครงสร้างโหนดสำหรับรายการเชื่อมโยงทางเดียว
class Node<T> {
final T value;
Node<T> next;
Node(T value) {
this.value = value;
}
}
// Building a chain: 1 -> 2 -> 3
Node<Integer> head = new Node<>(1);
head.next = new Node<>(2);
head.next.next = new Node<>(3);การนำ Iterator ไปใช้
สร้างคลาสภายในที่นำ Iterator<T> ไปใช้ โดยมีตัวชี้ตำแหน่งชี้ไปยังโหนดปัจจุบัน
import java.util.Iterator;
import java.util.NoSuchElementException;
class LinkedList<T> implements Iterable<T> {
private Node<T> head;
private int size;
private class LinkedListIterator implements Iterator<T> {
private Node<T> current = head; // cursor
@Override
public boolean hasNext() {
return current != null;
}
@Override
public T next() {
if (!hasNext()) throw new NoSuchElementException();
T value = current.value;
current = current.next;
return value;
}
}
@Override
public Iterator<T> iterator() {
return new LinkedListIterator();
}
}addFirst และ LinkedList ฉบับสมบูรณ์
เพิ่มความสามารถในการนำโหนดมาต่อไว้ด้านหน้า และดูคลาสที่ทำงานได้อย่างสมบูรณ์
class LinkedList<T> implements Iterable<T> {
private Node<T> head;
private int size;
public void addFirst(T value) {
Node<T> node = new Node<>(value);
node.next = head;
head = node;
size++;
}
public void addLast(T value) {
Node<T> node = new Node<>(value);
if (head == null) { head = node; }
else {
Node<T> curr = head;
while (curr.next != null) curr = curr.next;
curr.next = node;
}
size++;
}
public int size() { return size; }
@Override
public Iterator<T> iterator() {
return new LinkedListIterator();
}
}การใช้ Iterator แบบกำหนดเอง
เมื่อนำอินเทอร์เฟซ Iterable ไปใช้แล้ว รายการเชื่อมโยงจะทำงานกับลูปวนซ้ำสมาชิกและ forEach ได้
LinkedList<String> list = new LinkedList<>();
list.addLast("Alice");
list.addLast("Bob");
list.addLast("Charlie");
// For-each loop works!
for (String name : list) {
System.out.println(name);
}
// Alice
// Bob
// Charlie
// Stream also works (Java 8+)
list.forEach(name -> System.out.println("Hello, " + name));ตัววนซ้ำช่วง
ตัวอย่างที่ง่ายกว่า: ตัววนซ้ำเหนือลำดับตัวเลข โดยไม่ต้องมีโครงสร้างข้อมูลรองรับ
class IntRange implements Iterable<Integer> {
private final int start, end, step;
IntRange(int start, int end, int step) {
this.start = start; this.end = end; this.step = step;
}
IntRange(int start, int end) { this(start, end, 1); }
@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
int current = start;
public boolean hasNext() { return current < end; }
public Integer next() {
if (!hasNext()) throw new NoSuchElementException();
int val = current;
current += step;
return val;
}
};
}
}
for (int n : new IntRange(0, 10, 2)) System.out.print(n + " ");
// 0 2 4 6 8ตัววนซ้ำแบบท่องตามลำดับกลางของต้นไม้
การนำตัววนซ้ำแบบท่องตามลำดับกลางของ BST ไปใช้โดยใช้สแตกอย่างชัดแจ้ง ซึ่งแสดงให้เห็นว่าตัววนซ้ำสามารถแทนที่การท่องแบบเรียกซ้ำได้อย่างไร
import java.util.*;
class BinaryTree<T extends Comparable<T>> {
private record TreeNode<T>(T val, TreeNode<T> left, TreeNode<T> right) {}
private TreeNode<T> root;
public Iterator<T> inorderIterator() {
Deque<TreeNode<T>> stack = new ArrayDeque<>();
pushLeft(root, stack);
return new Iterator<>() {
public boolean hasNext() { return !stack.isEmpty(); }
public T next() {
TreeNode<T> node = stack.pop();
pushLeft(node.right(), stack);
return node.val();
}
};
}
private void pushLeft(TreeNode<T> node, Deque<TreeNode<T>> stack) {
while (node != null) { stack.push(node); node = node.left(); }
}
}ตัววนซ้ำแบบประเมินค่าเมื่อจำเป็น
ตัววนซ้ำสามารถสร้างค่าเมื่อจำเป็นได้ โดยสร้างเฉพาะเมื่อเรียก next() เหมาะสำหรับลำดับที่ไม่มีที่สิ้นสุด
class FibonacciIterator implements Iterator<Long> {
private long a = 0, b = 1;
@Override public boolean hasNext() { return true; } // infinite!
@Override public Long next() {
long result = a;
long next = a + b;
a = b;
b = next;
return result;
}
}
Iterator<Long> fib = new FibonacciIterator();
for (int i = 0; i < 10; i++) System.out.print(fib.next() + " ");
// 0 1 1 2 3 5 8 13 21 34ตัววนซ้ำแบบกรอง
ตัววนซ้ำแบบตัวตกแต่งที่ห่อตัววนซ้ำอื่นและข้ามสมาชิกที่ไม่ตรงตามเงื่อนไขคัดเลือก
import java.util.*;
import java.util.function.*;
class FilterIterator<T> implements Iterator<T> {
private final Iterator<T> source;
private final Predicate<T> predicate;
private T next;
private boolean hasNext;
FilterIterator(Iterator<T> source, Predicate<T> predicate) {
this.source = source; this.predicate = predicate;
advance();
}
private void advance() {
hasNext = false;
while (source.hasNext()) {
T candidate = source.next();
if (predicate.test(candidate)) { next = candidate; hasNext = true; break; }
}
}
public boolean hasNext() { return hasNext; }
public T next() { T val = next; advance(); return val; }
}
List<Integer> nums = List.of(1,2,3,4,5,6,7,8,9,10);
Iterator<Integer> evens = new FilterIterator<>(nums.iterator(), n -> n % 2 == 0);
while (evens.hasNext()) System.out.print(evens.next() + " ");
// 2 4 6 8 10การเชื่อมต่อ Iterator กับ Stream
สามารถปรับตัววนซ้ำแบบกำหนดเองให้ใช้กับสตรีมได้ด้วย Spliterators.spliteratorUnknownSize()
import java.util.*;
import java.util.stream.*;
Iterator<Integer> rangeIt = new IntRange(1, 6).iterator();
Stream<Integer> stream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(rangeIt, Spliterator.ORDERED),
false // not parallel
);
int sum = stream.mapToInt(Integer::intValue).sum();
System.out.println(sum); // 15การนำสมาชิกออกระหว่างการวนซ้ำ
เมธอดทางเลือก remove() ของ Iterator จะนำสมาชิกที่คืนค่าจากการเรียก next() ครั้งล่าสุดออก และต้องนำไปใช้อย่างชัดแจ้งในตัววนซ้ำแบบกำหนดเอง
class MutableLinkedList<T> implements Iterable<T> {
// ... (full implementation)
// Iterator with remove support
private class RemovableIterator implements Iterator<T> {
private Node<T> prev = null;
private Node<T> current = head;
public boolean hasNext() { return current != null; }
public T next() {
prev = (prev == null) ? null : current;
T val = current.value;
current = current.next;
return val;
}
public void remove() {
// Remove the last returned node
if (prev == null) head = current;
else prev.next = current;
size--;
}
}
}รายการตรวจสอบ Iterator
เมื่อนำ Iterator แบบกำหนดเองไปใช้:
- เรียก
hasNext()ก่อนnext()เสมอ - เมื่อไม่มีสมาชิกเหลืออยู่ ให้
next()โยนNoSuchElementExceptionแทนการคืนค่า null - ทำให้ตัววนซ้ำไม่มีสถานะที่ขึ้นกับคอลเลกชัน โดยอย่าเก็บขนาดของคอลเลกชันไว้ในแคช
- ใช้ modCount เพื่อตรวจจับการแก้ไขพร้อมกันหากจำเป็น
ตรวจสอบความเข้าใจอย่างรวดเร็ว
เมื่อไม่มีสมาชิกเหลืออยู่ next() ควรโยนอะไร
ทบทวน: การนำ Iterator แบบกำหนดเองไปใช้
ประเด็นสำคัญ:
- นำ Iterator
ไปใช้ด้วย hasNext(), next() และ remove() ซึ่งเป็นทางเลือก - เก็บฟิลด์ตัวชี้ตำแหน่งไว้ในตัววนซ้ำ โดยให้ชี้ไปยังสมาชิกถัดไป
- ให้ next() โยน NoSuchElementException เมื่อ hasNext() เป็น false
- สร้างอินสแตนซ์ตัววนซ้ำใหม่สำหรับการเรียก iterator() แต่ละครั้ง เพื่อให้ตัวชี้ตำแหน่งเป็นอิสระต่อกัน
- ตัววนซ้ำแบบประเมินค่าเมื่อจำเป็นจะสร้างค่าตามคำขอ เหมาะสำหรับลำดับที่ไม่มีที่สิ้นสุด
- ห่อตัววนซ้ำด้วย StreamSupport.stream() เพื่อเชื่อมต่อกับ Stream API
คำถามที่พบบ่อย
บทเรียน “การสร้าง Iterator แบบกำหนดเอง” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การสร้าง Iterator แบบกำหนดเอง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การสร้าง Iterator แบบกำหนดเอง”
สร้างคลาส iterator แบบกำหนดเองสำหรับรายการเชื่อมโยงหรือโครงสร้างช่วงค่าอย่างง่าย คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “การสร้าง Iterator แบบกำหนดเอง” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม
ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- สัญญาของ Iterable และ Iterator
- การสร้าง Iterator แบบกำหนดเอง
- ListIterator และการวนดูแบบสองทิศทาง
- Iterator แบบหยุดเร็วกับแบบปลอดภัย