[Java] Interface Iterator
JavaCollection 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]
참고 서적: 자바의 정석 3판 2