0Pricing
Java Academy · 강의

사용자 정의 반복자 구현

간단한 연결 리스트나 범위 구조를 위한 사용자 정의 반복자 클래스를 만듭니다.

사용자 정의 반복자 구현은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

사용자 지정 Iterator

사용자 지정 Iterator를 만들면 데이터 구조를 순회하는 방식을 완전히 제어할 수 있습니다. 이 레슨에서는 연결 리스트 Iterator를 단계별로 구현합니다.

Node 클래스

먼저 단일 연결 리스트의 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 구현

현재 Node를 가리키는 커서가 있는 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

Node를 앞에 추가하는 기능을 넣고 완전히 작동하는 클래스를 확인합니다.

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 인터페이스를 구현하면 연결 리스트를 for-each 반복문과 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));

Range Iterator

더 간단한 예로, 기반 데이터 구조 없이 숫자 범위를 순회하는 Iterator를 살펴봅니다.

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

트리 중위 순회 Iterator

명시적 스택을 사용해 중위 순회 BST Iterator를 구현합니다. 이를 통해 Iterator가 재귀 순회를 대신할 수 있음을 확인할 수 있습니다.

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(); }
    }
}

지연 Iterator

Iterator는 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

필터링 Iterator

다른 Iterator를 감싸고 조건식과 일치하지 않는 요소를 건너뛰는 데코레이터 Iterator입니다.

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와 스트림 연동

사용자 지정 Iterator는 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

순회 중 제거

Iterator의 선택 사항인 remove() 메서드는 마지막으로 next()를 호출해 반환된 요소를 제거합니다. 사용자 지정 Iterator에서는 이 메서드를 명시적으로 구현해야 합니다.

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를 구현할 때:

  • next()를 호출하기 전에 항상 hasNext()를 호출하십시오
  • 비어 있을 때 next()에서 null을 반환하지 말고 NoSuchElementException을 발생시키십시오
  • Iterator가 컬렉션의 상태에 종속되지 않도록 하십시오(컬렉션 크기를 캐시하지 마십시오)
  • 필요한 경우 modCount를 사용해 동시 수정을 감지하십시오

빠른 확인

더 이상 요소가 없을 때 next()는 어떤 예외를 발생시켜야 합니까?

복습: 사용자 지정 Iterator 구현

핵심 내용:

  • hasNext(), next(), 선택 사항인 remove()를 사용해 Iterator를 구현합니다
  • Iterator에서 다음 요소를 가리키는 커서 필드를 유지합니다
  • hasNext()가 false일 때 next()에서 NoSuchElementException을 발생시킵니다
  • 독립적인 커서를 위해 iterator()를 호출할 때마다 새 Iterator 인스턴스를 생성합니다
  • 지연 Iterator는 필요할 때 값을 생성하므로 무한 시퀀스에 유용합니다
  • Iterator를 StreamSupport.stream()으로 감싸 Stream API에 연결합니다

자주 묻는 질문

“사용자 정의 반복자 구현” 강의는 무료인가요?

네 — “사용자 정의 반복자 구현” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“사용자 정의 반복자 구현”에서 뭘 배우나요?

간단한 연결 리스트나 범위 구조를 위한 사용자 정의 반복자 클래스를 만듭니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“사용자 정의 반복자 구현” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Iterable과 Iterator 계약
  2. 사용자 정의 반복자 구현
  3. ListIterator와 양방향 순회
  4. 빠른 실패 반복자와 안전한 반복자
← Java Academy(으)로 돌아가기