jingyulog

Collection Framework 순회 - Iterable, Iterator 본문

Language/Java

Collection Framework 순회 - Iterable, Iterator

jingyulog 2025. 11. 30. 16:41

개요

자료 구조에서 순회는 자료 구조에 들어있는 데이터를 차례대로 접근해서 처리하는 것을 의미한다.

그런데 다양한 자료 구조가 있고, 각각의 자료 구조마다 데이터를 접근하는 방법이 다 다르다. 가령 배열 리스트는 index를 size까지 차례대로 증가시키면서 순회해야 하고, 연결 리스트는 node.next를 사용해서 node의 끝이 null일 때 까지 순회해야 한다. 이렇듯 각 자료 구조마다 순회하는 방법이 서로 다르기 때문에 각 자료 구조의 순회 방법을 배워야 한다. 그리고 순회 방법을 배우려면 각 자료 구조의 내부 구조도 알아야 한다. 하지만 우리는 단지 자료 구조에 들어있는 모든 데이터를 단순히 순서대로 접근해서 출력하거나 계산하고 싶을 뿐이다.

따라서 결국에 우리는 자료 구조의 구현과 관계 없이 모든 자료 구조를 동일한 방법을 순회할 수 있는 일관성 있는 방법이 필요한 것이다. 자바는 이런 문제를 해결하기 위해 Iterable, Iterator 인터페이스를 제공한다.

 

Iterable(반복 가능한), Iterator(반복자)

Iterable 인터페이스의 주요 메서드는 다음과 같으며, 단순 Iterator 반복자를 반환한다.

public interface Iterable<T> {
	Iterator<T> iterator();
}

Iterator 인터페이스의 주요 메서드에는 hasNext(), next()가 있고, 각각 다음 요소가 있는지 확인하고, 다음 요소가 없으면 false를 반환하고, 다음 요소를 반환하고, 내부에 있는 위치를 다음으로 이동시키는 역할을 한다.

public interface Iterator<E> {
	boolean hasNext();
    E next();
}

 

그럼 이제 Itreable, Iterator를 사용하는 자료 구조를 하나 만들어보자. 둘 다 인터페이스여서 구현체가 필요하다.

이때, Iterator는 단독으로 사용할 수 없으므로, Iterator를 통해 순회의 대상이 되는 자료 구조인 MyArray라는 배열을 가지는 단순한 자료 구조를 만들었다. 이 자료 구조는 Iterable 인터페이스를 구현하기 때문에 이 인터페이스는 이 자료 구조에 사용할 반복자인 Iterator를 반환하면 되므로, MyArrayIterator를 반환하도록 하면 된다. 이때, MyArrayIterator는 생성자를 통해 MyArray의 내부 배열인 numbers를 참조한다.

package src.iterable;

import java.util.Iterator;

public class MyArrayIterator implements Iterator<Integer> {
    private int currentIndex = -1;
    private int[] targetArr;

    public MyArrayIterator(int[] targetArr) {
        this.targetArr = targetArr;
    }

    @Override
    public boolean hasNext() {
        return currentIndex < targetArr.length - 1;
    }

    @Override
    public Integer next() {
        return targetArr[++currentIndex];
    }
}

import java.util.Iterator;

public class MyArray implements Iterable<Integer> {
    private int[] numbers;

    public MyArray(int[] numbers) {
        this.numbers = numbers;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new MyArrayIterator(numbers);
    }
}

import java.util.Iterator;

public class MyArrayMain {
    public static void main(String[] args) {
        MyArray myArray = new MyArray(new int[]{1, 2, 3, 4});

        Iterator<Integer> iterator = myArray.iterator();
        System.out.println("iterator.getClass() = " + iterator.getClass());

        while (iterator.hasNext()) {
            Integer nextValue = iterator.next();
            System.out.println("nextValue = " + nextValue);
        }
    }
}

// 실행 결과
iterator.getClass() = class src.iterable.MyArrayIterator
nextValue = 1
nextValue = 2
nextValue = 3
nextValue = 4

정리하면,

  • MyArray는 Iterable(반복할 수 있는) 인터페이스를 구현한다. 따라서 MyArray는 반복할 수 있다는 의미가 된다.
  • Iterable 인터페이스를 구현하면 iterator() 메서드를 구현해야 한다. 이 메서드는 Iterator 인터페이스를 구현한 반복자를 반환한다. 여기서는 MyArrayIterator를 생성해서 반환했다.

클래스 구조도

 

런타임 메모리 구조도

MyArrayIterator는 인스턴스를 생성할 때 순회할 대상을 지정해야 한다. 여기서는 MyArray의 배열을 지정했고, MyArrayIterator 인스턴스는 이제 MyArray의 배열을 참조해서 MyArray가 가진 내부 데이터를 순회할 수 있다.

MyArrayIterator 작동 방식

  1. currentIndex = -1 -> next() 호출 -> currentIndex = 0으로 증가 -> 1 반환
  2. currentIndex = 0 -> next() 호출 -> currentIndex = 1으로 증가 -> 2 반환
  3. currentIndex = 1 -> next() 호출 -> currentIndex = 2로 증가 -> 3 반환
  4. currentIndex = 2 -> next() 호출 -> currentIndex = 3으로 증가 -> 4 반환
  5. hasNext() 호출시, currentIndex(3) == MyArray.numbers.length(4) - 1 이므로, 종료된다.

 

향상된 for문

foreach라고 불리는 향상된 for 문은 자료 구조를 순회하는 것이 목적이다. 자바는 Iterable 인터페이스를 구현한 객체에 대해서 향상된 for문을 사용할 수 있게 해준다.

// Iterable과 향상된 for문
System.out.println("for-each 사용");
for (Integer value : myArray) {
    System.out.println("value = " + value);
}

for-each 사용
value = 1
value = 2
value = 3
value = 4

따라서 위와 같이 구현한 코드를 자바는 컴파일 시점에 다음과 같이 코드를 변경하므로, 두 코드는 같은 코드가 된다. 물론 모든 데이터를 순회한다면 둘 중에 더 깔끔한 향상된 for 문을 사용하는 것이 좋다.

while (iterator.hasNext()) {
	Integer value = iterator.next();
    System.out.println("value = " + value);
}

 

자바가 제공하는 Iterable, Iterator

자바는 Collection 인터페이스의 상위에 Iterable을 확장하도록 두고, 각각의 구현체에 맞는 Iterator도 구현해두었다. 따라서 모든 컬렉션 내의 자료 구조들은 Iterable과 Iterator를 사용해서 순회할 수 있다. 물론 Iterable을 구현하기 때문에 향상된 for문도 사용할 수 있다.

 

  • java.util.ArrayList$Itr : ArrayList의 Iterator는 ArrayList의 중첩 클래스이다.
  • java.util.HashMap$KeyIterator : HashSet 자료 구조에서는 사용하는 HashMap 자료 구조의 내부 클래스이다.
package src.iterable;

import java.util.*;

public class JavaIterableMain {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        Iterator<Integer> listIterator = list.iterator();

        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(2);
        set.add(3);
        Iterator<Integer> setIterator = set.iterator();

        printAll(listIterator);
        printAll(setIterator);

        foreach(list);
        foreach(set);
    }

    private static void foreach(Iterable<Integer> iterable) {
        System.out.println("iterable = " + iterable);
        for (Integer i : iterable) {
            System.out.println(i);
        }
    }

    private static void printAll(Iterator<Integer> iterator) {
        System.out.println("iterator = " + iterator);
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

iterator = java.util.ArrayList$Itr@3fee733d
1
2
3
iterator = java.util.HashMap$KeyIterator@4617c264
1
2
3
iterable = [1, 2, 3]
1
2
3
iterable = [1, 2, 3]
1
2
3

 

Iterator 디자인 패턴

Iterator 디자인 패턴은 객체 지향 프로그래밍에서 컬렉션의 요소들을 순회할 때 사용되는 디자인 패턴이다. 이 패턴은 컬렉션의 내부 표현 방식을 노출시키지 않으면서도 그 안의 각 요소에 순차적으로 접근할 수 있게 해준다. 이러한 Iterator 패턴은 컬렉션의 구현과는 독립적으로 요소들을 탐색할 수 있는 방법을 제공하며, 이로 인해 코드의 복잡성을 줄이고 재사용성을 높일 수 있게 해준다.

 

정리

Iterable은 반복 가능한이라는 뜻이고, Iterator는 반복자라는 뜻이다. 따라서 우리가 만든 MyArray는 Iterable을 구현했으므로, MyArray는 반복 가능한이라는 뜻이 된다. 그리고 MyArray가 반복 가능하기 때문에 iterator를 반환하고, for each문도 작동한다.

정리하면, 특정 자료구조가 Iterable, Iterator를 구현한다면, 해당 자료 구조를 사용하는 개발자는 단순히 hasNext(), next() 또는 for-each문을 사용해서 순회할 수 있고, 이는 자료 구조가 아무리 복잡해도 해당 자료 구조를 사용하는 개발자가 동일한 방법으로 매우 쉽게 자료 구조를 순회할 수 있게 해준다는 장점을 준다. 이것이 인터페이스가 주는 장점이다.

물론 자료 구조를 만드는 개발자 입장에서는 Iterable, Iterator를 구현해야 하니 번거롭겠지만, 해당 자료 구조를 사용하는 개발자 입장에서는 편리하다는 장점을 준다.

'Language > Java' 카테고리의 다른 글

Generic  (0) 2025.10.04
자바 프로그램이 실행되는 흐름  (0) 2025.10.04
제네릭 메서드가 필요한 이유  (0) 2023.09.22
ThreadLocal 동기화 적용 및 주의사항  (0) 2023.09.21
동시성 문제와 ThreadLocal  (0) 2023.09.21