Dico

[Java] Interface Iterator

  • 민갤

Collection Framework

데이터 군을 저장하는 클래스들을 표준화한 설계

컬렉션 Collection :  다수의 데이터. 데이터 그룹

프레임웍 Framework :  표준화된 프로그래밍 방식.

Interface Iterator

모든 컬렉션에 저장된 요소를 접근하는 데 사용되는 인터페이스.

단방향으로 이동 → 재사용 X. 마지막 요소에 다다르면 더 이상 사용할 수 없다.

Iterator iterator()

Collection 인터페이스에 정의된 메서드.

Iterator를 구현한 클래스의 인스턴스를 반환한다.

List나 Set 인터페이스를 구현하는 컬렉션마다 각 컬렉션의 특징에 알맞게 작성되어 있다.

public interface Collection<E> extends Iterable<E> {
   
    Iterator<E> iterator();
   
...

사용

  • List, Set 인터페이스를 구현한 컬렉션 클래스

    컬렉션 클래스에 대해 iterator를 호출하여 Iterator를 얻는다.

  • Map 인터페이스를 구현한 컬렉션 클래스

    키와 값을 각각 따로 Set의 형태로 얻어온 후 Set 인스턴스의 iterator()를 호출해서 얻는다.

Map<String, Integer> map = new HashMap<>();

Iterator<String> it1 = map.keySet().iterator();
Iterator<Entry<String, Integer>> it2 = map.entrySet().iterator();

  • 주로 while문을 사용해서 컬렉션 클래스의 요소들을 읽어온다.
List<String> list = new ArrayList<>();

Iterator<String> it = list.iterator();

while (it.hasNext()) {
    System.out.println(it.next());
}

method

반환타입이름설명
  boolean  hasNext()  읽어 올 요소가 남아있는 지 확인. 있으면 true
  E  next()  다음 요소를 읽어온다. 반드시 hasNext() 후에 사용
  void  remove()  next()로 읽어 온 요소를 삭제. 선택적 기능
  void  forEachRemaining(Consumer<? super E> action)  남아있는 요소들에 대해 지정된 작업을 수행

•  remove()

    반드시 next와 같이 사용해야 한다.

    next() 없이 호출하면 IllegalStateException이 발생한다.

재사용성

공통 인터페이스를 정의해서 표준을 정의하고 구현 → 코드의 일관성을 유지하여 재사용성을 극대화했다.

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

List 인터페이스를 구현한 다른 클래스로 바꿔야할 때 선언문만 변경하면 된다.

참조변수의 타입이 List이므로 List에 정의되지 않은 메서드는 사용되지 않았을 것이 확실하기 때문.

따라서 List에 없고 특성 클래스에만 있는 메서드를 사용하는 게 아니라면, List 타입의 참조변수로 선언하는 것이 좋다.

Enumeration

Iterator의 구버전.

컬렉션 프레임웍이 만들어지기 이전에 사용하던 것으로, 이전 버전으로 작성된 소스와의 호환을 위해 남겨졌다.

Iterator와 메서드 이름만 다를 뿐 기능은 같다.

반환타입이름설명Iterator
  boolean  hasMoreElements()  읽어 올 요소가 남아있는 지 확인. 있으면 true  hasNext()
  E  nextElement()  다음 요소를 읽어 온다  next()

ListIterator

Iterator에 이전 방향으로의 접근 기능을 추가한 것.

양방향 이동 → 각 요소간의 이동이 자유롭다.

List 인터페이스를 구현한 컬렉션에서만 사용 가능하다.

반환타입이름설명
  boolean  hasNext()  읽어 올 요소가 남아있는 지 확인. 있으면 true
  E  next()  다음 요소를 읽어 온다. hasNext()가 선행되야 안전하다.
  boolean  hasPrevious()  읽어 올 이전 요소가 남아 있는 지 확인. 있으면 true
  E  previous()  이전 요소를 읽어 온다. hasPrevious() 선행되어야 안전하다.
  int  nextIndex()  다음 요소의 index 반환
  int  previousIndex()  이전 요소의 index 반환
  void  remove()  next()/previous()로 읽어 온 요소를 삭제. 선택적 기능
  void  set(E e)  next()/previous()로 읽어 온 요소를 지정된 객체로 변경. 선택적 기능
  void  add(E e)  컬렉션에 새로운 객체를 추가. 선택적 기능

선택적 기능 Optional operation

반드시 구현하지 않아도 된다.

구현하지 않을 경우, 예외를 던져서 구현되지 않은 기능이라는 것을 메서드를 호출하는 쪽에 알리는 것이 좋다.

구현 Example

  • Test.java

      MyVector의 세부 코드는 Vector 글에 있다.

package blog;

import java.util.Iterator;

public class Test extends MyVector implements Iterator {
    int cursor = 0; // 앞으로 읽어 올 요소의 index
    int lastRet = -1; // 최근에 읽어 온 요소의 index. -1 == 읽어온 값이 없다.

    public Test(int capacity) {
        super(capacity);
    }

    public Test() {
        this(10);
    }

    @Override
    public synchronized String toString() {
        String tmp = "";
        Iterator it = iterator();

        for (int i = 0; it.hasNext(); i++) {
            if (i != 0)
                tmp += ", ";
            tmp += it.next();
        }
        return "[" + tmp + "]";
    }

    @Override
    public synchronized Iterator iterator() { // 초기화
        cursor = 0;
        lastRet = -1;
        return this;
    }

    @Override
    public boolean hasNext() {
        return cursor != size();
    }

    @Override
    public Object next() {
        Object next = get(cursor);
        ;
        lastRet = cursor++;
        return next;
    }

    @Override
    public void remove() {
        if (lastRet == -1) { // 더 이상 삭제할 것이 없으면
            throw new IllegalStateException();
        } else {
            remove(lastRet); // 최근에 읽어 온 요소 삭제
            cursor--; // 삭제 후 자리 이동이 일어남으로 감소
            lastRet = -1; // 읽어온 요소가 삭제되었으므로 초기화
        }
    }
}

  • Main.java
package blog;

import java.util.Iterator;

public class Main {

    public static void main(String[] args) {
        Test test = new Test();
        test.add(0);
        test.add(1);
        test.add(2);
        test.add(3);

        System.out.println(test);

        Iterator it = test.iterator();
        it.next();
        it.remove();
        it.next();
        it.remove();

        System.out.println(test);
    }
}
[0, 1, 2, 3]
[2, 3]

// Test의 remove()에서 cursor--;가 없다면
[0, 1, 2, 3]
[1, 3]

Example

마이크로소프트 아웃룩과 같이 단순히 읽어오기만 할 때는 next()를 사용하고,

읽어온 후 삭제할 때는 next()와 함께 remove()를 사용한다.

package blog;

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

public class Main {

    public static void main(String[] args) {
        ArrayList<Integer> original = new ArrayList<>(10);
        ArrayList<Integer> copy1 = new ArrayList<>(10);
        ArrayList<Integer> copy2 = new ArrayList<>(10);

        for (int i = 0; i < 10; i++) {
            original.add(i);
        }

        Iterator it = original.iterator();
        while (it.hasNext()) {
            copy1.add((int) it.next());
        }

        print("original : " + original);
        print("copy1 : " + copy1);

        it = original.iterator(); // Iterator는 재사용이 안되므로 다시 얻어와야 한다.
        while (it.hasNext()) {
            copy2.add((int) it.next());
            it.remove();
        }

        print("original : " + original);
        print("copy2 : " + copy2);
    }

    public static void print(String str) {
        System.out.println(str);
    }
}
original : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
copy1 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
original : []
copy2 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Iterator API

참고 서적: 자바의 정석 3판 2