이전의 Collection Framework 포스팅에서 각각의 코드를 살펴보면 Collection Class를 통해 자료를 저장한 뒤 반환하여 출력 시에 Iterator를 사용하였다. 이번 포스팅에서는 Iterator등 자바(Java)의 Iteration 기능들을 살펴본다.
1. Enumeration
Enumeration은 JDK 1.0 부터, 즉 초창기의 자바(Java) 버전에서부터 제공되던 Iteration 용 인터페이스이다. 지금까지 사용한 Iterator와 동일한 역할을 하지만 내부의 method가 조금 다르다.
Iterator는 hasNext() / next() 라는 method를 사용하였다면 이것은 hasMoreElements() / nextElement() 를 사용하여 동일한 작업을 수행한다.
그런데, 이 Enumeration은 기존의 코드와의 호환성을 위해서 남아있는 것이기 때문에 Iterator를 사용하는 것이 더욱 권장되며 최신의 Collection Class들에서는 사용이 안된다(Vector, HashTable에서만 가능).
또한 Enumeration은 요소의 제거(remove)를 지원하지 않는다. 사용 방법은 아래의 코드를 살펴보자.
package com.test;
import java.util.Enumeration;
import java.util.Vector;
public class EnumerationTest {
public static void main(String[] args){
Vector<Integer> v = new Vector<>();
v.add(10);
v.add(20);
v.add(30);
v.add(40);
Enumeration e = v.elements();
while(e.hasMoreElements()){
System.out.print(e.nextElement() + ", ");
}
// 결과 : 10, 20, 30, 40,
}
}
2. Iterator / Iterable
Iterator는 Java 1.2에서 소개된 인터페이스로 Universal Iterator, 즉 모든 Collection Class에서 사용가능한 Iteration 기능이다.
현재 자바(Java)에서는 Collection에 저장된 요소를 읽어오는 방법을 Iterator 인터페이스로 표준화해서 구현하고 있다. Collection Framework의 최상위 인터페이스인 Collection 인터페이스가 이 Iterator를 구현하였기에 List / Set / Queue등의 자료구조에서 사용할 수 있게 되었다.
또한 기존의 Enumeration에 더해 remove 기능이 추가되었다. 그러나 여전히 요소 값 변경(Replacement) / 첨가(Addition) 기능은 지원되지 않는다.
실제 Collection 인터페이스를 살펴보면 Iterable 이라는 인터페이스를 상속한 것을 알 수 있다. Iterable이라는 것은 Java 1.5에서 소개된 것(이 때, Generic / enhanced for loop 추가됨)으로 Iterator를 제공하는 method를 보유한 인터페이스다.
이 Iterable 인터페이스를 적절히 구현한 Class는 Enhanced For loop(for-each loop)를 사용해 반복문을 구현할 수 있게 된다. Collection 인터페이스는 Iterator를 사용하기 위해 Iterable 인터페이스를 상속받고 있다.

간단한 예를 통해 이해해보자. 이전 포스팅에서 나온 ArrayList는 List 인터페이스를 구현했고, List 인터페이스는 Collection 인터페이스를 상속했다.
그리고 Collection 인터페이스는 Iterable 인터페이스를 상속받고 있는데, 그래서 ArrayList의 내부 코드를 보면 Iterator<E> iterator()가 구현되어 있는 것을 확인할 수 있다. 아래의 사진을 보자.

잘 보면 Iterator를 ArrayList 내의 inner Class 형태로 구현하여 iterator method가 그 Class의 Instance를 반환하는 것을 알 수 있다.
이와 같이 모든 Collection Framework의 Collection Class에서 사용할 수 있도록 내부적으로 iterator를 구현하여 사용하도록 표준화 되어 있다.
복습하는 차원에서 아래의 코드를 통해 Iterator를 통한 요소 값 반환의 예제를 보자. 아래의 코드를 보면 hasNext method를 통해 다음으로 pointer가 넘어갈 수 있으면 pointer를 넘기는 방식으로 진행된다.
package com.test;
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayListTest {
public static void main(String[] args){
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next() + ", ");
}
// 결과 : 10, 20, 30, 40,
}
}
참고로, Java 1.8 부터는 default method로 forEach 구문이 추가되어서 아래와 같이 사용할 수도 있다.
package com.test;
import java.util.ArrayList;
public class Test {
public static void main(String[] args){
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.forEach(System.out::println);
}
}
// 결과
10
20
30
40
3. ListIterator
ListIterator는 JDK 1.2 에서 부터 제공되는 인터페이스로 Iterator 인터페이스를 상속 받아서 추가적인 기능이 더해진 인터페이스 이다.
List 인터페이스를 구현한 ArrayList / LinkedList 에서만 사용 가능한데, 이전의 Enumeration / Iterator에서는 한 방향으로의 요소 값 반환만 가능했다.(nextElement, next). 즉, 다음 pointer에 있는 요소 값만 반환이 가능했다.
ListIterator는 양 방향으로 iteration이 가능하여 역방향으로의 반환이 가능하다. 그래서 Iterator에 비해 6개의 method가 더 추가되어 구현되어 있는데 그 리스트는 아래와 같다.
method 명 | 기능 |
int nextIndex() | 다음 next() 메소드를 호출 시 반환될 요소의 index 반환 |
E previous() | 리스트의 이전 요소를 반환하고, 커서(cursor) 위치를 역방향으로 이동 |
int previousIndex() | 다음 previous() 메소드를 호출 시 반환될 요소의 인덱스를 반환 |
boolean hasPrevious() | 이 리스트 반복자가 해당 리스트를 역방향 순회 시 다음 요소를 갖는 다면 true 없다면 false 반환 |
void set(E e) | next() / previous() 메소드에 의해 반환된 가장 마지막 요소를 전달된 객체로 대체 |
void add(E e) | next() / previous() 메소드에 의해 반환된 가장 마지막 요소를 리스트에서 제거 |
위를 보면 일단 기본적으로 Previous로 넘어갈 수 있는 기능이 구현되어 있고 그에 따른 index 값도 가져올 수 있도록 구성되어 있다.
거기에 더 눈여겨볼 부분은 Iterator에서도 없었던 set / add method가 구현되어 있어서 iterator 를 하는 도중 값을 추가하거나 대체할 수 있게 되었다는 것이다.
다음의 코드를 통해 사용법을 간단히 익혀보자.
package com.test;
import java.util.ArrayList;
import java.util.ListIterator;
public class Test {
public static void main(String[] args){
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
ListIterator<Integer> listIterator = list.listIterator();
while(listIterator.hasNext()){
System.out.println("[Index : " + listIterator.nextIndex() + ", Value : " + listIterator.next() + "]");
if(listIterator.nextIndex() == 2){
listIterator.add(100);
}
}
System.out.println("---------------------------------------");
while(listIterator.hasPrevious()){
if(listIterator.previousIndex() == 2){
listIterator.remove();
}
System.out.println("[Index : " + listIterator.previousIndex() + ", Value : " + listIterator.previous() + "]");
}
System.out.println("---------------------------------------");
while(listIterator.hasNext()){
System.out.println("[Index : " + listIterator.nextIndex() + ", Value : " + listIterator.next() + "]");
}
}
}
//결과
[Index : 0, Value : 10]
[Index : 1, Value : 20]
[Index : 3, Value : 30]
[Index : 4, Value : 40]
---------------------------------------
[Index : 4, Value : 40]
[Index : 3, Value : 30]
[Index : 2, Value : 100]
[Index : 1, Value : 20]
[Index : 0, Value : 10]
---------------------------------------
[Index : 0, Value : 10]
[Index : 1, Value : 20]
[Index : 2, Value : 100]
[Index : 3, Value : 40]
결과를 통해서 볼 수 있듯이, iteration 도중 add를 하더라도 next pointer로 출력하지는 못한다. remove의 경우 현재의 pointer가 가리키는 요소를 제거했음을 확인할 수 있다.
오류가 있는 부분은 댓글로 남겨주시면 반영하겠습니다. 감사합니다.