반응형
Enumeration : 자바 초기버젼에서 개발된 것으로, HashTable과 Vector에서 사용가능
Iterator : jdk 1.2버젼에서 개발된 것으로, Collection 인터페이스를 구현한 모든 클래스에서 사용가능.

API를 확인해볼까요??
Vector와 HashTable에 있는 메소드입니다.
사용자 삽입 이미지
(HashTable에는 Enumeration 타입의 key()메소드도 존재합니다.)

하지만, ArrayList, LinkedList, HashMap에서는 찾아볼 수 없었습니다.
iterator는 HashTable이든 HashMap이든, Vector든 LinkedList든,
모두 쓸 수 있다는 것이죠.

Iterator에 대해서는 저번에 알아봤으니까.
이번에는 Enumeration의 API를 간단하게 뒤져봐야겠네요.

hasMoreElements

boolean hasMoreElements()
Tests if this enumeration contains more elements.
Returns:
true if and only if this enumeration object contains at least one more element to provide; false otherwise
Iterator의 hasNext()와 똑같은 일을 하는군요!!

nextElement

E nextElement()
Returns the next element of this enumeration if this enumeration object has at least one more element to provide.
Returns:
the next element of this enumeration.
Throws:
NoSuchElementException - if no more elements exist.
다음 elements가 존재하지 않으면, Exception이 발생하네요 ㅋㅋ
이것 또한 Iterator의 next()와 같아 보이는군요..ㅋㅋ

Vector의 요소들을 출력하는 예제입니다.
for (Enumeration e = v.elements() ; e.hasMoreElements() ;) {
System.out.println(e.nextElement());

}
자 그럼, 본격적으로 차이점에 대해서 알아보도록 합시다.

Snap Shot
Enumeration과 Iterator의 가장 큰 차이점은 바로 Snap Shot입니다.
Enumeration은 Snap Shot을 지원합니다. 여기서 'fail-fast
'라는 개념이 나오는데요.
Iterator는 fail-fast 방식이라고 합니다.
그럼 과연 이것이 무엇일까요?? 좋은 아티클이 있어서 긁어왔습니다...-ㅅ-;;

Fail-Fast

자바 2 이전 버전에서 사용되던 Vector, Hashtable의 뷰 객체인 Enumeration은 fail-fast 방식이 아니었으나, 자바 2의 콜렉션 프레임워크에서 콜렉션 뷰인 Iterator, ListIterator 객체는 fail-fast 방식이라는 점이다.

콜렉션 뷰는 콜렉션 객체에 저장된 객체들에 대한 순차적 접근을 제공한다. 그러나, 뷰 객체인 Enumeration 또는 Iterator 객체를 얻고 나서 순차적 접근이 끝나기 전에 뷰 객체를 얻은 하부 콜렉션 객체에 변경이 일어날 경우, 순차적 접근에 실패하게 된다. 여기서 변경이라는 것은 콜렉션에 객체가 추가되거나 제거되는 것과 같이 콜렉션 구조의 변경이 일어나는 경우를 말한다.

이런 상황은 멀티쓰레드 구조와 이벤트 구동 모델에서 일어날 수 있으며, 개발자가 혼자 테스트할 경우 발견하기 어려운 문제이다. 따라서 정확한 이해와 예상이 필요하며, 이에 대한 대처 방안을 마련해야 한다.

하부 콜렉션 객체에 변경이 일어나 순차적 접근에 실패하면 Enumeration 객체는 실패를 무시하고 순차적 접근을 끝까지 제공한다. Iterator 객체는 하부 콜렉션 객체에 변경이 일어나 순차적 접근에 실패하면 ConcurrentModificationException 예외를 발생한다. 이처럼 순차적 접근에 실패하면 예외를 발생하도록 되어 있는 방식을 fail-fast라고 한다.

Iterator는 fail-fast 방식으로 하부 콜렉션에 변경이 발생했을 경우, 신속하고 결함이 없는 상태를 만들기 위해 Iterator의 탐색을 실패한 것으로 하여 예외를 발생하며, 이렇게 함으로써 안전하지 않을지도 모르는 행위를 수행하는 위험을 막는다. 왜냐하면 이러한 위험은 실행 중 불특정한 시간에 멋대로 결정되지 않은 행위를 할 가능성이 있기 때문에 안전하지 않다고 할 수 있기 때문이다.


멋집니다..-ㅅ-; 더이상 설명할 것이 없을 것 같은데요.
흐음.. 일단 정리를 해보면, iterator는 일종의 pointer가 있습니다.
예전에 LinkedList 개념을 그림으로 그려보았는데요.
그거랑 매우 비슷합니다.

사용자 삽입 이미지

포인터가 Array의 자기자신과 다음 객체를 가르키는 것이지요.
그래서 위에 아티클에 써져있는 것처럼 삭제나 수정시 참조무결성 원칙에 어긋나게되어서
에러가 발생하게 되는것입니다.

아티클에 SnapShot에 대한 좋은 내용도 있더군요.

스냅샷

이처럼 콜렉션 뷰 객체인 Iterator와 ListIterator 객체가 fail-fast 방식인 이유는 스냅샷에 대한 보장을 포함하고 있지 않기 때문이다. 즉, 콜렉션의 뷰인 Iterator 객체를 얻었을 때 그 순간의 상태를 따로 생성하지 않기 때문에, Iterator 객체에서 순차적으로 하부 콜렉션 객체를 접근할 때, 콜렉션 객체의 변경에 영향을 받게 된다.

따라서, 콜렉션 뷰인 Iterator를 통하여 순차적 접근을 행하는 도중에 하부 콜렉션 객체에 변경이 일어날 가능성이 있는 경우 스냅샷을 명시적으로 생성하는 것이 좋다. ArrayList를 이용한 스냅샷을 생성하는 간단한 방법은 다음과 같다.

public Iterator snapshotIterator(Collection collection) {
return new ArrayList(collection).iterator();
}

이렇게 스냅샷을 이용하면 스냅샷이 생성된 순간의 상태에 대하여 Iterator를 통한
콜렉션의 뷰를 생성하게 된다. 하부 콜렉션의 변경이 일어나도 스냅샷에는 반영되지
않으며 Iterator는 실패없이 실행된다.

콜렉션 계열 중에 ArrayList를 사용하는 이유는 다른 콜렉션 계열 클래스들보다 생성과순차적 접근이 빠르기 때문이다. ArrayList가 삽입과 삭제에 불리한 단점이 있으나
이 경우에는 삽입과 삭제가 없으므로 고려되지 않는다.


조금 더 쉽게 정리를 해보자면,
Enumeration은 객체를 복사해서 저장합니다. 그래서 삭제나 수정시 전체 콜렉션에는
큰 타격을 주지 않는다는 것이죠.

Synchronization

제가 원래 알고 있던 지식은 "Enumeration은 동기화를 지원한다." 였는데,
어디서 잘 못 주워들었나봅니다. -ㅛ-
아티클에 <<콜렉션 프레임워크의 클래스들은 동기화를 보장하고 있지 않다.>>
라고 써져있더라구요. 그래서 찾아보았습니다. 역시 API에서 Enumeration부분을 봐도,
동기화에 대해서 정의된 부분은 찾을 수 없더라구요.
하지만,  Vector와 ArrayList의 차이점을 공부한 글에서도 알 수 있을 듯이
Vector는 동기화가 아주 넘쳐납니다-ㅅ-; 그러한 사실은 이클립스에서 Vector.class를
가보면, synchronized로 도배가 되어있는 것을 볼 수 있답니다.

아티클에는,
<<멀티쓰레드에서 콜렉션 구현 객체에 동시에 추가, 삭제를 위한 접근이 행해진다면, 그러한 행위가 발생하는 부분에 동기화를 명시적으로 해주어야 한다.>>
라고 써져있더군요. 방법까지 친절하게 명시해주고 있었습니다.
요렇게-ㅅ-;

Collection c = Collections.synchronizedCollection(myCollection);

결론적으로, Enumeration과 Iterator 사이에서 뚜렷하게 차이를 보이는 점은
 "스냅샷을 지원하느냐 하지 않느냐" 라는 것 같습니다.
아티클에서는 "Fail-fast Iterator에 대한 멀티쓰레드 무결성 해결방법" 이라는 제목아래 글을 써내려가고 있는데요. 스냅샷과 동기화가 Iterator의 멀티쓰레드 무결성 해결방법으로 제시되었고, 그와 함께 예시 소스코드도 보여주고 있습니다. 그러니까 Iterator가 스냅샷을 지원하지 않아도, 스냅샷을 명시적으로 생성할 수 있다는 것입니다. 이런건 한번 소스코드를 작성해봐야 확실히 알 수 있겠네요. 일단 이번 포스팅은 여기서 마치고, 소스코딩을 야심차게 시작해보도록 하겠습니다.-ㅛ-; 아자 +ㅅ+ ㅋ
< 출처: http://happystory.tistory.com >

+ Recent posts