glenn-syj / more-effective-java

이펙티브 자바를 읽으며 자바를 더 효율적으로 공부합니다
4 stars 5 forks source link

[MEJ-013] Iterator와 Iterable 인터페이스 보충 정리 #221

Open FickleBoBo opened 2 months ago

FickleBoBo commented 2 months ago

Based on : #215 by @yngbao97


평상시 무심코 사용하던 iterator에 대해 탐구를 진행해주셔서 다시 돌아볼 수 있는 좋은 시간이었습니다.

최근 인프런 김영한 강사님의 Collection 강의를 수강하며 iterable과 iterator에 대한 파트가 있어서 해당 내용을 첨부합니다.

먼저 자바 언어를 다루며 익숙하게 사용해온 향상된 for문(for each문)은 배열 또는 Iterable 인터페이스를 구현한 객체에 대해 사용할 수 있는 문법입니다. 알고리즘 학습을 통해 사용해온 List, Set 등의 자료구조는 전부 Collection 인터페이스를 상속받고 있고, Collection 인터페이스는 Iterable 인터페이스를 상속받고 있어서 해당 자료구조를 사용할 때 향상된 for문을 통해 자료를 순회할 수 있었습니다.

Iterable 인터페이스는 단순히 Iterator를 반환하며 Iterator 인터페이스는 hasNext 메서드와 next 메서드로 다음 요소의 존재 여부와 다음 요소 반환 및 위치 갱신의 역할을 수행합니다. (실제로는 조금 더 있습니다.)

이러한 Iterable 인터페이스를 구현하면 개발자가 만든 객체에 대해서도 순회하는 방법을 제공할 수 있습니다.

package collection.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];
    }
}

위 코드는 배열에 대한 iterator를 직접 구현한 간단한 구조입니다.

package collection.iterable;

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

위 코드의 MyArray 클래스는 배열을 필드로 갖고 방금 구현한 MyArrayIterator(반복자)를 반환하는 간단한 구조입니다.

package collection.iterable;

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 사용");

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

        // 배열이거나 iterable 인터페이스를 구현한 객체는 향상된 for문 사용 가능
        // 컴파일 시점에 아래 코드를 위 while문으로 변경해줌
        System.out.println("for-each 사용");
        for(int value : myArray){
            System.out.println("value = " + value);
        }
    }
}

// 출력
iterator 사용
value = 1
value = 2
value = 3
value = 4
for-each 사용
value = 1
value = 2
value = 3
value = 4

while문과 향상된 for문 모두 잘 작동하며 주석의 내용처럼 컴파일 시점에 변경된다고 합니다.

package collection.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);

        printAll(list.iterator());
        foreach(list);

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

        printAll(set.iterator());
        foreach(set);
    }

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

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

자바 컬렉션 프레임워크 자료구조에서 활용 예시입니다.


iterable을 구현한 객체에 대한 순회를 직접 테스트해보면 잘 작동하는 것을 볼 수 있습니다. StringTokenizer처럼 while문을 사용해서 순회를 하면 되고 iterable 인터페이스를 구현했으므로 향상된 for문을 사용해서 순회를 할 수도 있습니다.

교재와 탐구내용에 있었던 전통적인 for문과 iterator의 조합은 생소한 스타일의 문법이기는 했지만 처음부터 끝까지 단순히 순회밖에 할 수 없는 향상된 for문을 좀 더 다각도로 활용할 여지가 있는 것 같아서 좋은 내용이었습니다!


ref : 인프런 김영한의 실전 자바 중급 2편 - 컬렉션의 순회

yngbao97 commented 2 months ago

제 글을 스스로 쓰고있을 때까지만 해도 Collection이 Iterable을 상속받았고 그래서 향상된 for문으로 순회가 가능하다라는 개념이 조금 추상적으로 느껴졌는데, 간단한 구현예시를 보고나니 확실히 와닿네요. 향상된 for문으로 작성되었지만 컴파일 시점에 Iterator의 while문 형태로 변경된다는 점이 특히 흥미롭고 향상된 for문을 사용할 수 있다는 원리를 이해하는데 도움이 되었습니다! 단순히 구현되어있는 자료구조를 사용할 수도 있겠지만 필요가 있다면 Iterator 직접 구현해서 맞춤으로 사용해볼 수도 있을 것 같습니다. 이해하기 좋은 예시 공유해주셔서 감사합니다!