EEALL@ONCE

🔦이터레이터(iterator)가 뭔데? +이터레이터 패턴 본문

분류없음지식🔦

🔦이터레이터(iterator)가 뭔데? +이터레이터 패턴

올엣원스 2023. 12. 19. 20:55
728x90

이터레이터(iterator)는 컬렉션, 예를 들어 리스트나 세트와 같은 데이터 구조 내의 요소들을 순회하는 객체입니다. 이터레이터를 사용하면 컬렉션의 각 요소에 차례대로 접근할 수 있습니다. Java에서 이터레이터는 Iterator 인터페이스를 통해 구현됩니다. 주요 메소드는 다음과 같습니다:

  1. hasNext(): 이터레이터가 더 이상 순회할 요소가 있는지를 검사합니다. 요소가 있다면 true를, 없다면 false를 반환합니다.
  2. next(): 이터레이터의 다음 요소를 반환합니다.

간단한 사용 예제는 다음과 같습니다:

List<String> list = Arrays.asList("apple", "banana", "cherry");
Iterator<String> iterator = list.iterator();

while (iterator.hasNext()) {
    String element = iterator.next();
    System.out.println(element);
}

이 예제에서는 List 타입의 컬렉션을 생성하고, 해당 컬렉션을 순회하기 위해 Iterator 객체를 사용합니다. hasNext() 메소드로 더 이상 순회할 요소가 있는지 확인하고, next() 메소드로 다음 요소에 접근합니다.

이터레이터를 사용하는 것의 주요 장점은 컬렉션 구조에 대한 구체적인 지식 없이도 요소를 순회할 수 있다는 점입니다. 이는 코드의 유연성을 높이고 재사용성을 개선합니다. 그러나 반복자를 사용하면서 컬렉션을 수정하면 ConcurrentModificationException이 발생할 수 있는데, 이는 이터레이터의 한계점 중 하나입니다.

 

여기 이터레이터 개념을 설명하는 간단한 그림이 있습니다. 이 그림에서는 리스트 형태의 컬렉션과 그 컬렉션의 각 요소를 순회하는 이터레이터가 표현되어 있습니다. hasNext()next() 메소드가 어떻게 작동하는지 화살표를 통해 나타내고 있습니다.

 


이터레이터(iterator)를 생각해 볼 때, 상자 속에 여러 가지 장난감이 들어있다고 생각해 보세요. 이 상자는 마치 자바에서의 리스트나 세트 같은 거예요. 이터레이터는 이 상자 속에서 장난감을 하나씩 꺼내 보는 마법의 도구라고 할 수 있어요.

  1. hasNext()라는 마법은 상자 안에 더 많은 장난감이 있는지 알려줘요. 만약 상자 안에 장난감이 더 있다면 "네, 있어요!"라고 말하고, 장난감이 더 없다면 "아니요, 다 봤어요!"라고 말해요.
  2. next()라는 마법은 상자에서 다음 장난감을 꺼내줘요. 마치 "다음 장난감 보여주세요!"라고 말하는 것과 같아요.

이렇게 이터레이터는 상자 속의 장난감을 차례대로 모두 볼 수 있게 도와줘요. 하지만 주의할 점이 하나 있어요. 상자 속의 장난감을 보고 있는 동안에는 장난감을 상자에 넣거나 빼면 안 돼요. 그러면 마법이 깨져서 혼란스러워질 수 있거든요!


🧐 그럼 예를 들어 apple/ banna/ kiwi 가 있었다면, 이터레이터를 처음 돌 때, hasNext()에서는 뭘 확인해?

hasNext() 메소드의 원리는 이터레이터가 현재 위치에서 다음 요소가 존재하는지 확인하는 것입니다. 이 메소드는 true 또는 false 값을 반환하여, 이터레이터가 아직 순회할 요소가 남아 있는지를 알려줍니다.

예를 들어, "apple", "banana", "kiwi"가 들어있는 리스트가 있고, 이터레이터로 이 리스트를 순회한다고 가정해봅시다. 이터레이터를 처음 사용할 때의 상황을 살펴보겠습니다.

  1. 이터레이터 생성: 리스트를 순회할 이터레이터가 생성되며, 이 때 이터레이터는 리스트의 첫 번째 요소인 "apple" 앞에 위치합니다.
  2. 첫 번째 hasNext() 호출: 이터레이터는 "apple"이 존재하는지 확인합니다. "apple"이 리스트의 첫 번째 요소이므로, hasNext()는 true를 반환합니다. 이는 "다음에 순회할 요소가 있다"는 의미입니다.
  3. next() 호출: hasNext()가 true를 반환했으므로, next()를 호출하여 "apple"을 순회합니다. 이제 이터레이터는 "banana" 앞에 위치하게 됩니다.

이 과정을 반복하면서 "banana"와 "kiwi"에 대해서도 동일하게 hasNext()와 next()를 사용하여 순회합니다. 리스트의 마지막 요소인 "kiwi"를 순회한 후, hasNext()는 더 이상 순회할 요소가 없으므로 false를 반환하게 됩니다. 이렇게 hasNext()는 이터레이터가 리스트의 각 요소를 안전하고 정확하게 순회할 수 있도록 도와줍니다.

 
 
 
 


🧐 이터레이터 패턴이란? 

이터레이터 패턴은 디자인 패턴 중 하나로, 컬렉션(예: 리스트, 세트) 내의 요소들을 순회하는 방법을 제공합니다. 이 패턴의 주요 목적은 컬렉션의 내부 표현 방식에 관계없이 요소들을 순차적으로 접근할 수 있게 하는 것입니다.

이터레이터 패턴의 주요 구성 요소:

  1. Iterator(이터레이터): 컬렉션의 요소에 접근하고 순회하는 인터페이스를 제공합니다. 주로 next(), hasNext(), 그리고 경우에 따라 remove() 메소드를 포함합니다.
  2. Concrete Iterator(구체적 이터레이터): Iterator 인터페이스를 구현하는 클래스로, 특정 컬렉션(예: 리스트, 트리)에 대한 순회 로직을 구현합니다.
  3. Aggregate(집합체): Iterator 객체를 생성하는 인터페이스를 정의합니다. 이는 일반적으로 컬렉션 객체에 의해 구현됩니다.
  4. Concrete Aggregate(구체적 집합체): Aggregate 인터페이스를 구현하는 클래스로, 실제 컬렉션(예: 리스트, 트리 등)을 나타냅니다. 이 클래스는 자신의 요소를 순회할 Iterator 인스턴스를 생성합니다.

이터레이터 패턴의 장점:

  • 추상화된 순회: 클라이언트는 컬렉션의 내부 구현을 몰라도 요소를 순회할 수 있습니다.
  • 단일 책임 원칙: 컬렉션의 관리와 요소 순회 기능이 분리되어 있어, 각각이 단일 책임을 갖습니다.
  • 유연성: 다양한 종류의 컬렉션에 대해 동일한 인터페이스를 사용하여 순회할 수 있습니다.
public interface Iterator {
    boolean hasNext();
    Object next();
}

public interface Container {
    Iterator getIterator();
}

public class NameRepository implements Container {
    public String names[] = {"Robert", "John", "Julie", "Lora"};

    @Override
    public Iterator getIterator() {
        return new NameIterator();
    }

    private class NameIterator implements Iterator {
        int index;

        @Override
        public boolean hasNext() {
            return index < names.length;
        }

        @Override
        public Object next() {
            if(this.hasNext()){
                return names[index++];
            }
            return null;
        }
    }
}

// 클라이언트 코드
NameRepository namesRepository = new NameRepository();
for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
    String name = (String)iter.next();
    System.out.println("Name : " + name);
}

이 예시에서 NameRepositoryContainer 인터페이스를 구현하고, NameIteratorIterator 인터페이스를 구현합니다. 클라이언트는 NameRepositorygetIterator() 메소드를 통해 이터레이터를 얻어 순회할 수 있습니다.

 


여러분이 큰 장난감 상자를 가지고 있다고요. 이 상자 안에는 로봇, 인형, 자동차, 블록 등 여러 가지 장난감이 가득해요. 여러분은 이 상자 안에 어떤 장난감들이 있는지 모두 알고 싶어요.

이터레이터 패턴은 마치 마법사 친구 같아요. 이 마법사는 상자 안에 있는 장난감을 하나씩, 차례대로 보여줍니다. 마법사는 먼저 "여기 더 장난감이 있어?"라고 물어보고, 만약 더 있으면 "그럼 다음 장난감 보여줘!"라고 말해요. 이렇게 해서 상자 안의 모든 장난감을 볼 수 있어요.

여러분이 이 마법사를 도와 상자 안의 장난감을 하나씩 꺼내 본다면, 모든 장난감을 순서대로 볼 수 있겠죠? 이게 바로 이터레이터 패턴이에요. 마법사는 여러분이 상자 안에 무엇이 있는지 걱정하지 않아도 되게 해줘요. 그냥 마법사가 장난감을 하나씩 보여주면 돼요!

이 이터레이터 패턴은 컴퓨터에서도 비슷하게 작동해요. 컴퓨터 안에 정보가 많이 들어 있는 '상자'가 있고, 이터레이터라는 '마법사'가 그 안의 정보를 순서대로 보여준답니다.


내가 이해한 이터레이터는..
set /map/list(collection) 요소들  안에 내용을 하나씩 꺼내서 확인하는 거다. 

 

이 그림은 프로그래밍에서 이터레이터 프로토콜의 개념을 보여줍니다. 컴퓨터 화면에 코드 블록의 순서가 표시되어 있고, 이터레이터가 일련의 요소들을 순회하는 모습이 나타나 있습니다. 이터레이터, 요소, 시퀀스 등의 레이블이 함께 표시되어 있어서 이터레이터 프로토콜의 작동 방식을 쉽게 이해할 수 있습니다.


🧐프로토콜이란?

프로토콜은 프로그래밍에서 특정한 규칙, 함수, 속성 등을 정의하는 계약 같은 것입니다. 프로토콜은 어떤 클래스나 구조체가 가져야 할 기능이나 행동을 정의하지만, 그 구현은 하지 않습니다. 클래스나 구조체는 이 프로토콜을 '채택'하고, 프로토콜이 정의한 기능이나 속성을 실제로 구현합니다.

예를 들어, 자동차를 생각해볼 수 있습니다. "운전 가능한 것(Drivable)"이라는 프로토콜이 있다고 가정해보세요. 이 프로토콜은 "앞으로 가기", "멈추기", "방향 전환하기" 같은 기능을 정의할 수 있습니다. 하지만, 이 프로토콜은 어떻게 앞으로 가고, 멈추고, 방향을 전환하는지 구체적으로 정의하지 않아요. 각각의 자동차 모델(클래스)은 "운전 가능한 것" 프로토콜을 채택하고, 자신만의 방식으로 이 기능들을 구현합니다. 예를 들어, 전기자동차는 전기 모터를 사용해서, 디젤 자동차는 디젤 엔진을 사용해서 이 기능들을 구현할 것입니다.

프로그래밍에서도 이와 유사합니다. 프로토콜은 어떤 기능이 필요한지 정의하지만, 그 기능이 실제로 어떻게 작동할지는 각 클래스나 구조체가 결정하고 구현합니다. 이렇게 함으로써 코드의 유연성과 재사용성을 높일 수 있습니다.


이터레이터 프로토콜은 객체가 컬렉션(예: 배열, 리스트)의 요소들을 하나씩 순회할 수 있도록 하는 규칙 또는 인터페이스를 말합니다. 이 프로토콜은 보통 두 가지 주요 기능을 정의합니다:

  1. hasNext(): 이 메소드는 순회할 다음 요소가 있는지 확인합니다. 순회할 요소가 더 있으면 true를, 없으면 false를 반환합니다.
  2. next(): 이 메소드는 컬렉션의 다음 요소를 반환합니다.

이터레이터 프로토콜을 구현하는 객체는 이 두 메소드를 통해 컬렉션의 요소들을 순차적으로 접근할 수 있게 됩니다. 이러한 방식으로, 컬렉션의 내부 구현 세부사항에 관계없이 일관된 방법으로 요소들을 순회할 수 있습니다.

예를 들어, Java에서 Iterator 인터페이스는 이터레이터 프로토콜을 구현합니다. Python에서는 __iter__()와 __next__() 매직 메소드를 통해 이터레이터 프로토콜을 구현할 수 있습니다.

이터레이터 프로토콜의 구현은 프로그래밍 언어에 따라 다를 수 있지만, 기본적인 원리는 컬렉션을 통한 순차적 접근을 가능하게 하는 것입니다. 이를 통해 프로그래머는 컬렉션의 내부 구조에 신경 쓰지 않고도 데이터를 효율적으로 처리할 수 있습니다.


🧐 이터러블한 객체란?

"이터러블(iterable)"한 객체란 간단히 말해 그 객체의 요소들을 하나씩 순회할 수 있는 객체를 의미합니다. 즉, 이터러블한 객체는 자신의 내부 요소들을 반복적으로 접근할 수 있는 구조를 가지고 있습니다.

이터러블한 객체의 가장 대표적인 예시는 배열이나 리스트와 같은 컬렉션 타입입니다. 이러한 객체들은 내부에 여러 요소들을 저장하고 있으며, 프로그래밍 언어에 따라 이 요소들을 순회하기 위한 특별한 메커니즘이나 구문을 제공합니다.

예를 들어, Python에서는 리스트, 튜플, 문자열 등이 이터러블한 객체입니다. 이들은 for 루프를 사용하여 각 요소를 순회할 수 있습니다:

my_list = [1, 2, 3, 4, 5]
for item in my_list:
    print(item)

Java에서는 Iterable 인터페이스를 구현하는 모든 객체가 이터러블합니다. 이 인터페이스는 iterator() 메소드를 정의하며, 이 메소드는 해당 객체의 요소들을 순회할 수 있는 이터레이터를 반환합니다:

List<Integer> myList = Arrays.asList(1, 2, 3, 4, 5);
for(Integer item : myList) {
    System.out.println(item);
}

이터러블한 객체를 사용하면 컬렉션의 내부 구현에 상관없이 일관된 방식으로 요소들에 접근할 수 있어 프로그래밍이 더욱 간결하고 유연해집니다.

728x90