반응형
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 >
반응형
아래 그림에서 볼 수 있듯이 Collection과 Map은 따로 존재합니다.
저는 지금까지 Collection 인터페이스를 Map, Set, List가 구현하는 줄 알았는데,
아니더라구요. 이러한 사실은 API를 봐도 알 수 있습니다.

사용자 삽입 이미지

                        List : 순서(index)를 알고 있는 컬렉션.
                        Set : 중복된 것을 허용하지 않는 컬렉션.
                        Map : 키-값 기능 제공.

이러한 개념은 실제 코드를 작성해봐야 알 수 있지요?
에자일 자바책 9장에 이러한 내용이 깊~~게 언급되므로
그때 좀 더 자세히 알아보도록 하겠습니다.-ㅅ-ㅎ


에자일자바 5장을 보면 Collections.sort()가 등장합니다.
Collection이라는 인터페이스 외에, Collections라는 유틸리스 클래스가 또 존재하는데요.
API를 살펴보니, Collection에는 메소드가 거의 없드라구요.
sort()또한 보이지 않았습니다.
sort()는 Collections에 static으로 선언이 되어있습니다.
아래 Collections API를 보면,
사용자 삽입 이미지
-ㅅ-;;;
난해합니다. -_ㅠ
일단 필드와 메소드를 보고 유추해본 결과,
Collections클래스는 List, Set, Map에서 공통으로 사용되는 메소드를 모은
유틸리티 클래스라고 볼 수 있지 않을까 생각됩니다.

static으로 선언되어 있기 때문에
Collections.sort()로 편하게 사용할 수 있습니다.

다음으로 정말 중요한 iterator()입니다.
iterator()는 Iterator타입의 객체를 리턴하는데요.
이 객체를 사용해서 콜렉션 안에 있는 모든 엘리먼트를 하나하나 검색할 수 있습니다.
참 바보스럽게도, 전 여지껏 Iterator라는 인터페이스 없이
iterator()메소드만 Collection, Set, List에 각각 존재하는 줄 알았습니다.-ㅅ-;
하지만, 아니더라구요 ㅎ API를 보면 알 수 있습니다.
Collection, Set, List 인터페이스는 Iterable인터페이스를 구현합니다.
Iterable인터페이스를 보면,
사용자 삽입 이미지
iterator()메소드가 있는것을 볼 수 있습니다.
보면, 메소드가 Iterator형인 것이 확인되지요??
짧은 영어로 해석을 해보면,
<T>타입 요소의 집합을 건네주는 iterator을 리턴한다.-ㅅ-;

그니까.. 제너릭으로 한정한 타입의 요소의 집합으로 iterator을 리턴한다는 뜻이겠지요?

그렇다면.. 이번에는 Iterator인터페이스를 뒤져보도록 하겠습니다.
다음과 같은 메소드들이 존재합니다.

사용자 삽입 이미지

boolean hasNext()
Returns true if the iteration has more elements. (In other words, returns true if next would return an element rather than throwing an exception.)
Returns:
true if the iterator has more elements.
=> 만약 다음에 불러올 요소들이 더 있다면 true을 리턴한다.
    다시말해서, 만약 next가 엘리먼트를 리턴하면 true를 리턴한다.

직역을 하면 도저히 이해하기가 힘들 것 같아서, 엄청난 의역을 해야만 했습니다.ㅎ
제가 이해한 바로는 next()메소드를 통해서 콜렉션에 저장된 다음 요소가 리턴되면
hasNext()는 true를 리턴하고, 리턴될 다음 요소가 없어도
exception은 발생하지 않는다는 것이지요..?ㅋㅋㅋ

그렇다면, 다음으로 next()메소드를 살펴봅시다. 이것을 보면 더욱 이해가 잘됩니다.
E next()
Returns the next element in the iteration. Calling this method repeatedly until the hasNext() method returns false will return each element in the underlying collection exactly once.
Returns:
the next element in the iteration.
Throws:
NoSuchElementException - iteration has no more elements.
=> 반복하면서 다음 요소를 리턴한다.
    이 메소드는 hasNext()메소드가 false를 리턴할 때까지
    기초가되는 콜렉션에 있는 각각의 요소를 정확하게 한번씩
    반복적으로 리턴할 것이다.

영어는 역시 어렵습니다..-_ㅠ
위에 hasNext()메소드에 대한 설명을 보충해주고 있지요??
이래서 API는 보고나면 재밌습니다... 볼때는 괴롭지만 ㅎㅎ

마지막으로 remove()가 있는데, 이것은 Collection안에 있는 모든 요소를 검색하여
어떤 조건을 만족하지 않는 요소를 제거할 때 사용하는데요.
좋은 소스코드가 있습니다.

                        static void filter(Collection c) {
                            Iterator i = c.iterator();
                            while (i.hasNext()) {
                            if (!cond(i.next())) i.remove();
                            }
                        }

                    hasNext()가 true일 경우에만 if문을 반복하는 것입니다.
                    제시하는 조건을 만족하지 않을경우,
                    remove()메소드를 사용해서 해당하는 요소를 제거해줍니다.

iterator() 이전에 Enumeration이 쓰였다고 하는데요.
iterator()와 Enumeration의 차이점은 다음에 포스팅 하도록 하겠습니다.
차이점을 공부하다보면, iterator()에 대해서도 더 자세히 알 수 있겠지요?
출처 : http://happystory.tistory.com

+ Recent posts