각각의 콜렉션 객체에 대해서 for문이나 뭐 이런식으로 콜렉션안에 들어가 있는
객체들을 참조 할 수 있지만, Iterator를 사용하면 소스의 구분없이
컬렉션안의 객체들을 참조 할 수 있습니다.
예를 들어서 ArrayList를 사용해서 객체를 관리하는 프로그램과 (혹은 클래스)
배열을 사용해서 객체를 관리하는 프로그램(혹은 클래스) 두개가 있는데..
이 두개를 합치는 경우를 생각해 보면
ArrayList와 배열에 대해서 각각 참조하고 있는 객체의 리스트를 얻기 위해서는
각 클래스에 맞는 형식으로 순환문을 돌려야 합니다.
ArrayList라면..
...
list.get(i);
...
}
배열이라면
...
배열[i];
...
}
이런식이 되겠죠..
이것을 Itreration을 사용하면 일괄적인 소스로 관리가 가능합니다.
그리고 또 하나는 컬렉션 객체를 숨길 수가 있습니다.
위의 예제 같은 경우에는 마음대로 list나 배열을 만질 수가 있습니다.
add도 할 수 있고 move도 할 수 있고요..
하지만 Iteration을 만들어 그것을 넘겨주게 되면
사용하는 측에서도 Iteration에 대한 사용만 고려하면 되고
객체를 숨겨서 직접 접근하지 못 하도록 할 수도 있습니다.
간단한 예제를 보겠습니다.
일단 관리하고 싶은 클래스입니다.
public class Character {
private String name;
private String age;
private String sex;
public Character(String name, String age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
그리고 위 클래스의 객체를 관리하게 될 클래스들입니다.
static final int MAX_VALUES = 6;
Character[] characters;
int index = 0;
public CharacterArray() {
characters = new Character[MAX_VALUES];
addItem("홍길동","22","남");
addItem("성춘향","19","여");
addItem("강감찬","45","남");
addItem("제다이","?","남");
}
public void addItem(String name, String age, String sex) {
if(index >= MAX_VALUES) {
System.out.println("array is full");
} else {
Character c = new Character(name, age, sex);
characters[index] = c;
index++;
}
}
public Character[] getCharacters() {
return characters;
}
}
import java.util.ArrayList;
public class CharacterList {
ArrayList<Character> characters;
public CharacterList() {
characters = new ArrayList<Character>();
addItem("탐크루즈","41","남");
addItem("안젤리나졸리","33","여");
addItem("디아나","31","여");
addItem("러셀크로","41","남");
}
public void addItem(String name, String age, String sex) {
Character c = new Character(name, age, sex);
characters.add(c);
}
public ArrayList<Character> getCharacters() {
return characters;
}
}
두개의 클래스는 각각 내부적으로 ArrayList와 배열을 사용해서 관리하고 있습니다.
여기서 각각의 클래스가 가지고 있는 Character의 객체들을 조회하는 소스를 만들어 보겠습니다.
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
CharacterList cl = new CharacterList();
CharacterArray ca = new CharacterArray();
ArrayList<Character> list = cl.getCharacters();
Character[] array = ca.getCharacters();
Character c = list.get(i);
System.out.println(c.getName());
System.out.println(c.getAge());
System.out.println(c.getSex());
}
System.out.println("############################");
Character c = array[i];
System.out.println(c.getName());
System.out.println(c.getAge());
System.out.println(c.getSex());
}
}
}
굵은 글씨로 된 부분을 보시면 직접 배열객체와 ArrayList를 받아오고 있습니다.
그리고 색깔이 다른 부분을 보면 각 캐릭터에 대해서 서로 다른 순환문을 사용하고 있습니다.
그래서 이 반복을 캡슐화 하는 것이 Itrerator 패턴입니다.
Iterator는 java.util에서 인터페이스가 제공됩니다.
그것을 사용해보겠습니다. 그러면 ArrayList에서는 이미 이 Iterator를 구현하고 있기 때문에 CharacterList 클래스는
그 Iterator를 return해주는 부분만 만들어주면 됩니다.
그리고, ArrayList를 리턴해주던 메소드는 삭제합니다.
import java.util.ArrayList;
import java.util.Iterator;
public class CharacterList {
private ArrayList<Character> characters;
public CharacterList() {
characters = new ArrayList<Character>();
addItem("탐크루즈","41","남");
addItem("안젤리나졸리","33","여");
addItem("디아나","31","여");
addItem("러셀크로","41","남");
}
public void addItem(String name, String age, String sex) {
Character c = new Character(name, age, sex);
characters.add(c);
}
public Iterator getIterator() {
return characters.iterator();
}
}
하지만 배열은 Iterator가 제공되지 않기 때문에 java.util.Iterator를 구현한 Iterator 클래스를 만들어야 합니다.
간단하게 만들어 보겠습니다.
import java.util.Iterator;
public class CharacterArrayIterator implements Iterator {
Character[] c;
int index = 0;
public CharacterArrayIterator(Character[] c) {
this.c = c;
}
public boolean hasNext() {
if(index >= c.length || c[index] == null) {
return false;
} else {
return true;
}
}
public Object next() {
Character character = c[index];
index++;
return character;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
본래대로라면 remoce() 메서드도 구현을 해줘야 합니다.
하지만 그냥 예외처리 하고 넘어가겠습니다.
그리고 CharacterArray에서 Iterator를 넘겨주는 부분을 구현해야 합니다. 그리고, 배열은 private으로 숨겨야겠죠..
import java.util.Iterator;
public class CharacterArray {
static final int MAX_VALUES = 4;
private Character[] characters;
int index = 0;
public CharacterArray() {
characters = new Character[MAX_VALUES];
addItem("홍길동","22","남");
addItem("성춘향","19","여");
addItem("강감찬","45","남");
addItem("제다이","?","남");
}
public void addItem(String name, String age, String sex) {
if(index >= MAX_VALUES) {
System.out.println("array is full");
} else {
Character c = new Character(name, age, sex);
characters[index] = c;
index++;
}
}
public Iterator getIterator() {
return new CharacterArrayIterator(characters);
}
}
그러면 이제 조회를 하는 클래스를 보겠습니다.
import java.util.ArrayList;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
CharacterList cl = new CharacterList();
CharacterArray ca = new CharacterArray();
Iterator i = cl.getIterator();
Iterator i2 = ca.getIterator();
showList(i);
showList(i2);
ArrayList<Character> list = cl.getCharacters();
Character[] array = ca.getCharacters();
}
public static void showList(Iterator i) {
while(i.hasNext()) {
Character c = (Character)i.next();
System.out.println(c.getName());
System.out.println(c.getAge());
System.out.println(c.getSex());
}
}
}
구현체에 따라서 나누어져있던 로직이 Iterator를 사용해서 처리가 되었습니다.
그리고 실제로 이 Test 클래스에서는 자기가 조회하고 있는 객체가
배열인지 ArrayList인지 Map인지 몰라도 상관이 없습니다.
단지 Iterator만 사용 할 뿐입니다.
자바에서 제공되는 API중 Collection 객체들은 이 Iterator를 구현하고 있습니다.
만약에 Character 클래스를 다른 방법으로 관리하는 또다른 클래스가 추가 된다고 하더라도
저 Iterator 인터페이스를 구현한 Iterator 클래스만 만들어주면 됩니다.
컬렉션 객체안에 있는 모든 항목에 접근 하는 방식이 통일 되면, 어떤 종류의 집합체에 대해서도 사용 할 수 있는
코드를 만들 수 있습니다.