자바 디자인 패턴 1 - Iterator

자바(JAVA)

1. iterator 패턴

프로그래밍을 하다 보면, array나 List, Set, Map과 같은 애들을 많이 씁니다. 얘네들의 특징은 어떤 데이터들의
집합체라는 겁니다. 원래 집합체란 게 속에 뭐가 들었냐가 중요하죠ㅋㅋ. 그래서 집합체들을 다룰 때는 얘들이
가지고 있는 개별 원소에 대해서 이런 저런 작업들을 할 일이 많습니다.
iterator를 쓰게 되면, 집합체와 개별 원소들간에 분리시켜 생각할 수가 있습니다.

2. 예제

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class MagicianList implements Iterable<String> {

   private List<String> list = new ArrayList<String>();

   public void add(String name){

       list.add(name);

   }

 

   public Iterator<String> iterator() {

      return new Iterator<String>(){

         int seq = 0;

         public boolean hasNext() {

            return seq < list.size();

         }

         public String next() {

            return list.get(seq++);

         }

         public void remove() {}

      };

   }

}

// 위에서 만든 클래스를 iterator 사용해 풀었다.
public static void main(String[] arg){
     MagicianList magicians = new MagicianList();
     magicians.add("이은결");
     magicians.add("Kevin parker");
     magicians.add("David Blaine");

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

먼저, main 함수의 황토색 부분을 보면, magicians 의 원소들을 뽑아내는데, magicians 라는 변수를
전혀 쓰지 않습니다. 물론, 내부적으로 iterator라는 변수가 magicians와 관계를 유지해주고 있긴합니다만,
일단 iterator를 가지고 온 후에는 데이터 집합체가 뭐냐에 신경을 쓸 필요가 없어진 거죠.
iterator만 가져오면, 걍 hasNext() , next() 만 가지고 반복하면서 원소들에 대해서 처리를 하면 됩니다.

3. Iterator관련 interface

소스 코드의 보라색 부분이 jdk 안에 있는 Iterator에 관한 부분입니다.
java.util.Iterable 이란 넘을 구현하고 있습니다. 고놈한테는 Iterator<E> iterator() 라는 메소드
한개만 있습니다. 뭔소리냐면, 이 클래스는 무슨무슨 집합체 데이터를 가꾸 있으니깐, iterator로 원소들을
뽑아다가 쓸 수 있도록 제공하겠다는거죠.

그담에 등장하는 것이 java.util.Iterator입니다. 소스 코드의 청록색 부분입니다.
method가 3개가 있죠? hasNext()는 다음 구성 요소가 있냐고 물어봅니다. next()는 그 요소를 뽑아옵니다.
remove()는 일부러 구현을 안했습니다. API에 보면, 마지막으로 꺼낸 요소를 제거한다.
(optional operation) 이라고 되어있습니다. optional이라는 걸 강조할라고 구현 안했습니다.
그리고 iterator를 돌면서 데이터를 삭제한다는 것은 그다지 바람직해 보이진 않습니다.
요기서 한가지 집고 넘어가야 할 것은 시퀀스는 hasNext()가 아니라 next()에서 증가시켜야 한다는 것입니다.
좀 비상식적인 얘기긴 합니다만, hasNext()를 호출하고 또 호출하는 일이 발생할 수도 있기 때문이죠.
hasNext라는 메소드 이름이, next를 가지고 있는지를 체크하겠다는 것이니까요.

4. JAVA API에 있는 Iterator

우리가 알고 있는 일반적인 집합체들은 전부 Iterator를 제공합니다. Set, List 등은 Collection 을 상속 받는데, Collection이 Iteratable을 상속 받기 때문입니다.
위에서 청록색 부분을 list.iterator() 라고 쭐여버려도 됩니다. 걍 있는 거 안 쓰고 굳이 구현한 건 예제 파일을
함 보여줄라고 한 겁니다. 사실은 예제 전체가 억지로 만들어낸 겁니다. 일반적인 집합체를 구현해서 쓰는 일은
거의 없고, JDK 안에 들어 있는 애들을 가져다 쓰는데, 걔들은 거의 대부분 Iterator를 제공하거든요.
(Map은 한 다리 건너서 제공합니다.) 그래서 Iterator를 직접 구현할 일은 거의 없습니다. 가져다가 쓸 일이
있을 뿐이죠.

이제 Map은 왜 Iterator를 제공하지 않는 지를 살펴보죠. Map은 Set이나 List와는 달리 key-value의 구조입니다. key에 대한 Iterator인지 value에 대한 Iterator인지 구별할 방법이 없죠. 그래서 아예 제공을 안 합니다.
그러나 Map에는 key에 대해서는 Set<K> keySet()이라는 key를 Set으로 가져오기를 지원하고, value에
대해서는 Collection<V> values() 를 제공합니다. 위에서 말씀드렸다시피 Set과 Collection은 둘다 Iterator를
제공합니다.

5. Enumeration vs Iterator

둘은 굉장히 유사합니다. Enumeration의 경우는 boolean hasMoreElements() 와 E nextElement() 를
제공합니다. Iterator의 hasNext() , next() 에 대응되는 메쏘드들이죠.
차이는 두 가집니다. 첫째 Iterator에는 remove()가 있다. 둘째, Iterator의 함수 이름이 훨씬 쉽다.
(타이핑 노가다가 쭐어든다.-_-; )
처음에 Enumeration이 나왔고, 그걸 쫌 편하게 만들어보자한 것이 Iterator랍니다.

Posted by 녹두장군