목차
Iterator Pattern(반복자 패턴): 직접 구현해 보기
반복자는 집합체의 저장 배열이나 인덱스를 외부에 공개하지 않고 순회 상태를 별도 객체가 관리하게 한다. hasNext()는 남은 원소가 있는지 확인하고, next()는 현재 원소를 반환한 뒤 위치를 전진시킨다. 두 반복자는 같은 집합을 서로 다른 위치에서 읽을 수도 있다.
![]()
그림에서 Aggregate가 반복자를 만들고 ConcreteIterator가 실제 이동을 담당한다. 아래 예제는 Java 표준 Iterable과 Iterator를 써서 같은 구조를 구현한다.
import java.util.Iterator;
import java.util.NoSuchElementException;
final class Shelf<E> implements Iterable<E> {
private Object[] elements = new Object[2];
private int size;
void add(E element) {
if (size == elements.length) {
Object[] grown = new Object[elements.length * 2];
System.arraycopy(elements, 0, grown, 0, size);
elements = grown;
}
elements[size++] = element;
}
@Override
public Iterator<E> iterator() {
return new Iterator<>() {
private int cursor;
public boolean hasNext() {
return cursor < size;
}
@SuppressWarnings("unchecked")
public E next() {
if (!hasNext()) throw new NoSuchElementException();
return (E) elements[cursor++];
}
};
}
}
class Main {
public static void main(String[] args) {
Shelf<String> shelf = new Shelf<>();
shelf.add("A");
shelf.add("B");
shelf.add("C");
for (String value : shelf) System.out.println(value);
}
}
초기 배열 길이는 2다. A, B가 들어간 뒤 C를 추가할 때 배열 길이를 4로 늘리고 기존 두 원소를 복사한다. 반복자의 cursor는 0에서 시작해 next()마다 증가하므로 출력은 A, B, C 순서다. hasNext()가 거짓인데 next()를 호출하면 NoSuchElementException을 던진다. 원본 구현에는 끝 검사가 없어 배열 범위 오류가 났고, 클래스 이름 ArrayList가 Java 표준 타입과 충돌했다.
이 코드는 순회 중 변경을 허용할지 정의하지 않은 학습용 구현이다. 순회 도중 add하면 새 원소가 보일 수도 있다. 실제 API라면 수정 시 예외를 던지는 정책, 생성 시 스냅샷을 만드는 정책, 변경을 실시간 반영하는 정책 중 하나를 정해야 한다. 대개의 애플리케이션에서는 직접 컬렉션을 만드는 대신 표준 List의 반복자를 사용한다.