2012년 11월 18일 일요일

자바의 열거맵(EnumMap)




EnumMap 클래스는 자바 5에서 추가된 내용이므로 새로울지 모르나 저번 강의시간에 열거셋에 대해서 이미 공부하였으므로 필자의 핵심예제를 통해 바로 습득하는 시간을 가져보겠다.^^


=== 강이의 JAVA강좌 ===
Map: {SUNDAY=S, MONDAY=M, TUESDAY=T, WEDNESDAY=W, THURSDAY=T, FRIDAY=F, SATURDAY=S}
Key: [SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY]
Value: [S, M, T, W, T, F, S]

예제를 간략하게 보면 오늘 출력하는 부분은 총 3가지로 이루어져 있다. 하나는 키와 밸류를 함께 출력하는 Map이고 다른 하나는 키만 출력하는 Key이고 나머지 하나는 밸류만 출력하는 Value이다. 예제는 거부감(?)을 없애기 위해 예전에 썼던 Day 열거형을 사용하였으니 금방 예제에 동화(?)되리라 본다.ㅎㅎ

열거맵이므로 열거형을 사용하며 예제에서는 그로 Day를 만들어서 사용하였다. 열거맵을 사용하려면 열거셋과 마찬가지로 열거형이름에 확장자로 class를 명명하면 열거형에 있는 요소들을 해당맵의 key로서 사용이 가능해진다. 우리는 열거맵을 공부하고 있으므로 이 열거요소들, 즉 키에 해당하는 각각의 밸류값들을 put( ) 메소드를 통해 넣어주었다.^^

다음 단락은 그렇게 고생고생(?)해서 만든 열거맵을 찍어주는 구문이다. 이제 이런 메소드들은 해보기도 하였지만 보기만봐도 다 이해가 될것이다. 그 이유중 하나는 필자가 이미 언급하였다시피 자바 컬렉션 프레임워크의 메소드들은 기능에 의미를 부여함은 물론 여러 컬렉션 클래스들에서 그 기능이 같거나 비슷할경우 해당 메소드 이름도 같거나 비슷하게 지었기 때문에 아주 생소한 이름이 아닌이상 의미파악이 어느정도 가능하기 때문에 그러하다.^^

끝으로 한마디 덧붙이자면 자바의 열거맵(EnumMap)은 비동기화(not synchronized) 되어있고 물론 Collections.synchronizedMap( ) 메소드를 이용하면 동기화 가능하고 반복자는 fail-fast 방식이고 열거맵의 순서는 열거형의 키를 기반으로 자연적인 순서(natural order)를 따른다. 여기서 자연적인 순서라함은 enum Day에서 열거한 순서대로 나열한다는 것을 말한다. 예를 들어, enum DAY에서 열거요소의 SUNDAY와 MONDAY를 바꿔놓으면 모든 결과들은 이 둘의 순서가 뒤바뀌어서 출력될 것이다. 여러분이 put( ) 메소드를 이용해서 밸류값을 넣은 순서와 상관없이 말이다.ㅎㅎ 그냥 그렇구나하고 넘어가지 말고 직접 해보면서 알아가는 여러분이 되기 바란다. 복습 잘하기 바라면서 열거맵 강좌는 여기서 마무리하겠다.^^

2012년 11월 13일 화요일

자바의 트리맵(TreeMap)


요소들을 차례대로 정렬하는 기능을 제외하면 해쉬맵(HashMap)과 거의 같은 트리맵(TreeMap)에 대해서 공부하는 시간을 갖기로 하겠다. 이미 트리셋을 공부하였으니 트리맵도 이름처럼 정렬을 가미한 클래스라는 것을 알수 있을 것이다.^^


자바 컬렉션 프레임워크의 멤버인 트리맵도 해쉬맵처럼 비동기화(not synchronized)되어 있고 Collections.synchronizedSortedMap 메소드를 이용해 wrapped해서
SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));
위의 구문을 이용해 동기화시키는 것이 가능하며 키(Key)값을 중심으로 밸류(value)값을 가지고 있고 NavigableMap 인터페이스와 SortedMap 인터페이스, 그리고 AbstractMap 클래스를 기본으로 계승하고 있다. 키값은 중복될수 없으며 밸류값은 여러개의 중복된 값을 가지는 것이 가능하다.^^


해쉬테이블과 해쉬맵까지 거쳐온 여러분들은 모두 알고있는 내용일테지만 워밍업(?) 차원에서 잠시 읊어보았다.ㅎㅎ 이제 필자의 예제를 보면서 트리맵에 대해서 살펴보기로 하겠다. 저번 강의를 들은 여러분은 아마 이런 생각을 하고 있을 것이다. 저번 예제 그냥 순서대로 정렬하는거 맞죠? 그래 맞당.ㅎㅎ 여러분이 다 이와같이 눈에 뻔한 예제에 식상할 것을 대비해 필자가 대규모 반전(?) 예제를 들고 나왔다. 간만에 대한민국 걸그룹들이 총출동하는 예제이니 눈에 쌍심지(?)를 켜고 들여다보면 얻는바가 클것이라 생각한다.^^


=== 강이의 JAVA강좌 ===
2ne1=YG
소녀시대=SM
시크릿=TS
원더걸스=JYP
카라=DSP
=== 강이의 자바강좌 ===
{카라=DSP, 원더걸스=JYP, 시크릿=TS, 소녀시대=SM, 2ne1=YG}
{소녀시대=SM, 시크릿=TS}

예제는 크게 두 파트로 나눴다. 기본적으로 트리맵을 이용해서 요소들을 돌리는 것이 첫번째 파트이고 두번째 파트는 응용편으로 한번 넣어보았다. 그럼 이제 예제로 들어가보자. 트리맵을 써야하니 처음에 불러들인 것이고 요소들을 트리맵에 넣어야하는데 요소갯수만큼의 put( ) 메소드들을 이용한 요소삽입은 해보았으니 이번엔 일반 배열을 통한 for 구문을 이용해 요소들을 트리맵에 위와같이 넣어보았다. 이렇게 하는거 궁금했었는데 필자가 보여준거라면 우린 통한거다.ㅎㅎ 여기까지가 첫번째 파트인데 잘 찍힌다. 그런데 여기서 중요한건 자알 찍히는 정도가 아니라 순서까지 맞춰서 찍혔다는 것이다. 트리맵이니깐......

다음은 descendingMap( ) 메소드를 이용해서 tm 트리맵을 역순으로 정렬하는 구문이다. 이런건 메소드의 이름만 봐도 바로 알수 있어야한다.ㅎㅎ 그리고 마지막은 subMap( ) 메소드인데 트리맵에서 이게 하이라이트(?)일지도 모르겠다. 조금 어려워보이는 걸로 따지면 말이다.ㅎㅎ 알고나면 별거없으니 모르는 이들은 잠시 귀를 기울이기 바란다.^^

subMap( ) 메소드가 하는 일은 sub에서 느끼다시피 뭔가 일을 부분적으로 하는 것이다. 여기서는 해당 맵 요소들의 범위를 정해주면 그 해당 범위내의 정렬된 요소들 중에서 일부분을 부분적으로 돌려준다. 예제에서는 '소녀시대'와 '원더걸스'를 입력해서 소녀시대부터 원더걸스까지, 즉 '소녀시대', '시크릿', '원더걸스'를 돌려준다. 그런데 결과는 원더걸스가 빠져있다. 왜일까? 힘쎈 원더걸스(?)라서 사라진건가? ㅎㅎ 범위를 정해줄때의 시작요소가 '소녀시대'이고 끝요소가 '원더걸스'인데 시작요소와 끝요소의 옆을 보면 true와 false가 적혀있을 것이다. true일때는 지정요소를 포함하는 것이고 false일때는 지정요소를 포함하지 않는다. 따라서 소녀시대 옆에는 true라서 시작요소를 포함한 것이고 끝요소인 원더걸스 옆에는 false라 결과에서 누락된 것이다. subMap( ) 메소드의 기능을 알아놓으면 프로그램을 짤때 도움이 될것이다.^^

이것으로 트리맵에 대해서 짧게나마 알아보는 시간을 가져보았다. 본 예제를 중심으로 응용하면서 다른 메소드들을 공부하면 전체적으로 무난할 것이라 생각한다. 다른 기능들은 대부분 여러분이 벌써 필자와 함께 훈련(?)한 부분들이거나 눈치코치(?)로도 다 이해가능한 수준일 것이기에 이 정도에서 오늘 트리맵 강의는 편안하게 접도록 하겠다.^^

2012년 11월 12일 월요일

자바의 해쉬맵(HashMap)


자바의 해쉬테이블을 열심히 공부하였다면 오늘 배울 해시맵이 유사하므로 저번보다는 비교적 수월할 것이니 가벼운 마음으로 따라와주길 부탁하면서 강의를 시작해보겠다.


JDK 1.2 버젼부터 컬렉션의 멤버로서 나온 해쉬맵(HashMap)은 해쉬테이블(Hashtable)과는 다르게 기본할당공간이 16이고 비동기화(not synchronized)되어 있어 thread-safety는 꽝이지만 null을 허용하며 단일쓰레드 환경이라면 해쉬테이블에 비해서 엄청나게 속도가 빠르다. 따라서 멀티쓰레드 환경이 아닌 단일쓰레드만 사용한다면 선택의 여지가 없다.
Map m = Collections.synchronizedMap(new HashMap(...));
위의 명령을 이용하면 해쉬맵에서도 객체에 대한 동기화(synchronized)가 가능해진다. 많은 이들이 맵을 공부할때 할당된 메모리 공간에 키에 대한 밸류값만 들어간다고 생각하는데 키값과 밸류값 모두 아래의 그림처럼 해당공간에 같이 저장하게됨을 알아두기 바란다.+


해쉬맵은 위에서 언급한 특이사항 몇가지만 빼고는 전에 배운 해쉬테이블과 비슷하니 바로 예제를 통해서 심화학습을 하기로 하겠다.^^


=== 강이의 JAVA강좌 ===
다=C
가=A
마=E
라=D
나=B
=== 강이의 자바강좌 ===
다=C
가=A
마=E
라=D
나=B
=== 강이의 JAVA강좌 ===

다=C
가=A
마=E
라=D
나=B


저번 강의를 열공(?)하였다면 그냥 보기만해도 이해가 팍팍가리라 생각한다. 보통 궁금해하는 것이 HashMap에서 어떻게 루프(loop)를 돌리는지에 대한 방법일텐데 대표적인 세가지 방법 모두 어떻게 돌리면 되는지 필자가 손수 필살예제(?)를 준비하였으니 마음껏 즐기기 바란다.ㅎㅎ

Enumeration 선언부분 명령이 약간 어려울수 있는데 java.util 패키지에 있는 Collections 클래스의 enumeration( ) 메소드를 이용하면 이렇게 루프를 돌릴수 있다. Iterator는 어떻게 사용하는지 다 알고 있으리라 보이지만 비교도 할겸 iterator( ) 메소드를 통해 다시 루프를 돌렸더니 아니나 다를까 같은 답이 나왔다.^^ 마지막에 있는 새로운 for loop 구문으로 돌리면 더 간단해진다.ㅎㅎ 저번 강의에서 공부하느라 지친 여러분들의 노고를 치하할겸 오늘은 여기서 끝내도록 하겠다.^^

자바의 해쉬테이블(Hashtable)


기본적으로 JDK 1.0 버젼에서부터 쓰여왔고 자바 2에서부터 컬렉션의 멤버로서 등재되기 시작한 해쉬테이블(Hashtable)이 다른 컬렉션들과 다른점은 동기화되어 있다는 점이다. 그럼 이제 맵(Map) 인터페이스를 구현한 해시테이블에 대해서 알아보도록 하겠다. 자바 컬렉션 프레임워크를 공부한 여러분은 이미 알고 있겠지만 Map은 컬렉션 즉, 컬렉션(Collection) 인터페이스를 계승하고 있지 않으므로 컬렉션에 관계된 모든 기능을 구현하고 있지는 않다. 또한 그동안 손쉽게 써오던 반복자(iterator)도 여기엔 없다. 대신에 Enumeration을 손쉽게 쓸수 있도록 구비되어 있다. 물론 반복자도 우회해서 쓸수있는 방법도 있다. 둘다 어떻게 쓸수 있는지 예제에서 다룰것이므로 깊은 체념이나 상심(?)은 자제하기 바란다.ㅎㅎ 그동안 배워온 리스트(List)나 셋(Set)이 요소들의 집합이나 순서 개념이 강하다면 오늘 배울 맵(Map)은 검색의 개념이 본격적으로 가미된 인터페이스라고 말할수 있겠다. 전화번호부처럼 이름을 찾아 전화번호를 찾는 과정을 떠올려보면 될것이다. 맵을 구현한 클래스를 다루는 첫번째 시간인만큼 해쉬테이블에 대해서 비교적 자세히 살펴보기로 하겠다. 오늘 강의를 잘 따라오면 앞으로 공부할 다른 종류의 맵 클래스들에 대해서도 공부하기가 한결 수월해질 것이다.^^


그림은 해쉬테이블에서 키를 활용해 값이 어떻게 저장되는지를 보여주고 있는데 들어가기전에 equals( ) 메소드와 hashCode( ) 메소드의 기본원리는 일단 알고 있어야한다. 두 객체가 equals( )의 결과가 같다면 해쉬코드는 반드시 같아야하며 두 객체가 equals( )의 결과가 다르다면 해쉬코드는 같을수도 있고 다를수도 있다. 압축설명하자면 해쉬테이블의 데이터구조에 대한 알고리즘은 key를 이용해서 내부적으로 equals( ) 메소드와 hashCode( ) 메소드가 연산하여 어느 주소를 가지는 메모리에 저장할지를 결정한다. 만일 해쉬코드가 같은 값을 가지게 된다면 같은 장소에 저장이 될것이다. 그럴경우 그림처럼 키값과 밸류값들을 연결리스트를 통해서 저장한다. 키가 달라도 해쉬코드는 같은 값을 가질수 있는데 해쉬테이블은 생성시 기본값으로 11개의 배열공간이 할당되며 이 배열공간의 길이로 방금 얘기한 해쉬코드 연산(키에 대한 해쉬코드값 % 11)을 통해 해당값들을 정해진 공간에 저장한다. 그림의 buckets는 값들을 저장할수 있는 메모리에 할당된 공간을 말하는데 배열구조랑 비교하자면 키(key)가 배열의 인덱스(index)라고 생각하면 이해가 쉬울 것이다. 그리고 그 키(key)와 관련 쌍을 이루어 저장되는 것이 값(value)이다.ㅎㅎ

해쉬테이블의 원리는 여러분의 건강(?)을 감안하여 이 정도로 하고 이제 예제를 보기로 하겠다.^^ 아직까지 뭐가뭔지 잘 모르겠다하는 이들은 끝까지 따라오기 바란다. 예제를 보면 모든 걱정이 기우(杞憂)에 지나지 않았다는 것을 깨닫게 될것이다. 아래의 예제가 좀 길어보일지 모르나 같은 내용을 다른 방법을 써가면서 같은 결과를 도출해내는 것이므로 실제는 예제의 반 정도만 여러분이 집중하면 될것이다. 혹시나 길다고 여기서 중도포기하는 이들(?)을 막기위해 한마디 남기면서 예제풀이로 들어가겠다. 그럼 아래의 해쉬테이블 예제를 보자.^^



자 시작해보겠다.ㅎㅎ 조금 설명이 길어질것 같은 불길한 예감(?)이 스쳐가고 있지만 최대한 간략하면서도 머리에 쏙쏙 들어오게 설명하겠으니 피곤하더라도 잠깐만 집중하기 바란다.^^ 먼저 프로그램에서 필요한 인터페이스나 클래스를 불러들이고 메인메소드에서 해쉬테이블의 인자 ht를 생성하고 키(key)와 값(value)을 put( ) 메소드를 이용해 집어넣었다. put 메소드안의 첫번째가 키이고 두번째가 값이다. 메소드의 형식을 빌어 다시 표현하자면 아래와 같다.

V put(K key, V value)

이 메소드 형식을 보면 참 재미있는 사실을 한가지 알수 있는데 앞에 V가 눈에 들어오지 않는가? (승리를 뜻하는게 아님.-) 당연히 value를 뜻하는 것이겠징? ㅎㅎ 키와 값을 넣어주면서 지금 넣어주는 키에 관련된 그전의 값이 있다면 리턴해 준다는 부분이다. 물론 없으면 null을 돌려준다. 조금 있다가 다시 나올텐데 미리 예고편(?) 때리고 있는거다.ㅎㅎ

다시 돌아가서 키와 값을 넣어주고 keys( ) 메소드와 elements( ) 메소드를 이용해 Enumeration과 함께 해쉬테이블을 돌려주니 키와 값이 자알~ 출력된다.^^ 순서는 해쉬테이블 마음가는데로 찍힌다.ㅎㅎ 열거에 대해선 안해본것도 아니니 별 어려움은 없을 것이라 보고 다음으로 가보겠다.

각종 메소드들을 통해 키와 값들을 함께도 찍고 따로도 찍어보고 하는 예제이다. 보기 좋으라고 제목 자체를 메소드 이름으로 달아놨으니 거침이 없을 것이다.ㅎㅎ 자 그럼 이제 아까 예고했던 본방송편(?)을 시작하겠다.

String s = (String)ht.put(3,"OK");

코드에 필자가 이렇게 만들어 놓았다. 키값으로 3을 주고 그 해당하는 키값으로 가서 OK를 value값으로 넣으라는거다. 그리고 그곳에서 쓰던 value값이 있었다면 넘겨주고 찍어보라는 예제인데 해보니까 C가 찍힌다. 키 3에 해당하는 값의 value값으로 C가 있었으니까 말이다. String은 Object 값을 넘겨받으므로 캐스팅 처리해준것 뿐이다. ht를 찍어보니 키 3의 위치에 방금 넣었던 OK가 제대로 출력되는 것을 알수 있고 또한 예전에 있던 키 대신에 같은 키를 넣으면 나중값을 받아들인다는 것을 여기서 캐치할수 있어야한다. value값은 상관없지만 key값은 중복을 허용하지 않으니까 말이다. 너무 당연한가? 혹시나 해서~ㅎㅎ

다음은 contains( ) 메소드를 이용해서 value값인 OK가 해쉬테이블에 들어있냐고 체크하라고 하니 있으니까 있다고 true 돌려준거고 containsKey( ) 메소드는 키값으로 0이 있냐고 물어보니까 없어서 false라고 돌려준 것인데 어려울건 없다고 본다.^^

이번엔 putAll( ) 이라는 메소드가 나온다. 한마디로 괄호안에 있는 ht 해쉬테이블을 ht2로 카피하라는거다. 출력해보니까 잘 옮겨졌다. get( ) 메소드를 이용해서 키값으로 3에 해당하는 value값을 물어보니 OK라고 대답하고 너무 혹사시키는 것 같아 미안한 마음(?)에 편히 보낼려고 remove( ) 메소드를 이용해 키값인 3을 넣고 찍어보았더니 이제 해쉬테이블의 쌍(pair)이 예상대로 하나가 줄었다.ㅎㅎ 욕하는거 아니다.-

오케이 드디어 마지막 구문이다. 조금만 참아라~ Iterator를 이용해서 기존의 ht 해쉬테이블을 찍어보도록 만든 것인데 하나는 keySet( ) 메소드를 이용해서 키값과 밸류값을 출력한 것이고 다른 하나는 entrySet( )을 이용해서 키값과 밸류값을 한번에 처리한 것이다. 이 부분은 Iterator도 사용할수 있다는 것을 보여줄겸 한번 적어보았으나 어떤 것을 사용할지는 상황을 고려해야한다.

덧붙여 잠시 얘기해 보자면 컬렉션 클래스에서 저장된 요소들을 차례대로 접근하기 위한 방법을 컬렉션 뷰 메소드(collection view methods)라고 부른다. 그리고 fail-fast라는 것이 있는데 Enumeration(not fail-fast 방식)이나 Iterator(fail-fast 방식)를 사용하여 각 요소들에 대한 순차적 접근이 끝나기도 전에 해당 컬렉션에 대한 객체들에 대해서 요소가 추가되거나 삭제되거나 하는 어떤 변경이 가해질 경우 요소들에 대한 접근에 실패하게 되는데 이때 Enumeration과 Iterator가 대처하는 방식이 다르다. 멀티쓰레드 환경에서 이런 상황이 일어날수 있는데 이런 경우에 Enumeration은 그와 상관없이 요소들에 대한 순차적 접근을 끝까지 수행하는 not fail-fast 방식인 반면에 Iterator는 그럴 경우 예외(ConcurrentModificationException)를 발생시키며 안전할지 모르는 사태를 대비해 작동을 중지하는 fail-fast 방식을 취한다.

해쉬테이블에 대해서 공부해 봤는데 그리 쉽지는 않았을 것이다. key와 value를 세트로 거기다가 해슁까지 소화(?)하기가 부담스러울걸 고려해 가급적이면 무난하게 읽을수 있도록 풍부한 해설과 다양한 예제를 담으려고 필자가 각고한 노력(?)을 기울인만큼 여러분도 그에 필적하는 노력(?)으로 응답해 주기를 기대하면서 오늘 강의는 여기서 마치겠다. 수고많았다.^^

2012년 11월 6일 화요일

자바의 열거셋(EnumSet)

자바의 EnumSet은 이름에서 풍기듯이 Set 인터페이스를 기반으로 하면서 enum 열거요소들을 이용해서 보다 빠르고 강력하게 결과를 도출해낼수 있도록 자바 5에서 등장한 새로운 무기(?)중 하나라고 볼수 있다. JDK 1.5 이전 버젼에서 자바를 처음 접한 이들은 아직까지 생소할수 있겠지만 오늘 강이의 자바강좌와 함께 한다면 필자가 단언하건데 EnumSet을 어느정도 수준까지는 자유자재로 구사할수 있는 능력자(?)가 될 것이니 더도말고 30분만 본 강의에 집중해주기 바란다. 강이의 자바강좌와 함께 새로운 것을 계속해서 알아가고 있는 이들은 이런 신병기(?)가 출현했을 경우 눈을 더 크게 뜨고 강의를 집중해야 기존의 자바인들보다 뛰어난 능력을 한가지라도 보유할수 있는 기회의 장으로 만들수 있음을 명심해야 할것이다.ㅎㅎ

enum에 대해서 아예 모르거나 처음 들어본 이들은 예전에 올린 enum 열거형 타입에 대한 강좌를 다시 듣고 이 자리로 돌아오기 바란다. 여러분의 빠른 이해를 위해 예제도 저번 열거형 강좌의 예제를 그대로 도입해 EnumSet 버젼으로 승화시켰음을 알리는 바이다.ㅎㅎ

그럼 오늘 배울 EnumSet은 어떤 상황에 쓰는건지 궁금할텐데 말그대로 열거형 타입으로 지정해놓은 요소들을 가장 쉽고 빠르게 배열처럼 요소들을 다룰수 있는 기능을 제공한다. EnumSet은 기술상으로 원소갯수가 2^6 그러니까 64개를 넘지 않을 경우에 겉은 Set 기반이지만 내부적으로 long 데이터형의 비트필드를 사용하게 된다. 비트필드는 2의 제곱수들로 이루어져 메모리 공간도 적게 차지하고 속도도 빠른데 이런 원리를 기반으로 돌아가는 세트가 바로 EnumSet이다. HashSet의 경우에는 배열과 해쉬코드를 이용하는데 상황에 따라 다를수 있겠지만 통상 비트연산을 이용하는 EnumSet보다는 속도면에서 훨씬 뒤떨어질수 밖에 없다. 거기다 EnumSet은 Set을 기반으로 하지만 enum과 static 타입의 메소드들로 구성되어있어 안정성을 최대한 추구하면서도 편리한 사용이 가능하다.

서론은 이 정도로 하고 이제 예제로 살펴보는 시간을 갖도록 하겠다.^^


=== 강이의 JAVA강좌 ===
EnumSet Day: [SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY]
EnumSet Day: []
es: [WEDNESDAY, FRIDAY]
es2: [SUNDAY, MONDAY, TUESDAY, THURSDAY, SATURDAY]
es2: [TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]

위의 예제 하나로 EnumSet을 끝낼 것이다. 더도말고 메인메소드안에 있는 딱 12줄만 여러분이 이해하면 끝나는 것이다. 그냥 보기만 해도 알것이나 간략하게 부연설명을 해보도록 하겠다. 일단 첫 단락은 예전에 enum 타입으로 Day라는 열거형을 만들어본 적이 있는데 기억하는지 모르겠다. 일요일부터 토요일까지 요일을 기입하였는데 오늘 예제는 이를 토대로 플레이(?) 할것이니 잘 따라오기 바란다.ㅎㅎ

메인메소드의 첫 EnumSet을 보면 인자로 es를 만드는데 allOf( )라는 메소드를 이용해서 열거형 Day에 있는 요소들을 다 가져오라는 것이다. 쓰는 방식은 예제처럼 괄호안에 열거형 이름에다가 .class를 붙인 형태를 사용하면 된다. 이게 가능해지는 이유(?)는 컴파일한 지금 여러분의 자바파일이 있는 디렉토리에 가보면 해답을 찾을수 있을 것이다. 클래스명$Day.class라는 새로운 파일로부터 말이다. 열거형을 만들면 그와 관련된 .class가 이처럼 새로 생성된다. 둘째줄에 인자 es2는 es와 똑같은 세트를 만드는 것이다. 예제처럼 copyOf( )나 clone( ) 메소드를 이용해서 복사가 가능하다. 그래서 복사가 제대로 되었는지 es2를 찍어보니 열거세트 Day와 똑같이 출력되는 것을 보니 잘 되었다는 것을 알수 있다. 물론 카피했으니 es는 찍어보지 않아도 같은 세트임을 동시에 알수있고 말이다.^^ 그 다음에 noneOf( ) 메소드는 세트를 비우라는 것이다. 그래서 명령주고 찍어보니 세트가 제대로 텅 비게 되었다.ㅎㅎ

그럼 이제 다음 단락으로 넘어가 보겠다. of( ) 메소드는 해당 요소를 찾아서 넣으라는 것이다. 수요일과 금요일만 넣으라고 했으니 그것만 찍힌 것인데 당연하지만 여러분이 지정하는만큼 자유자재로 넣을수 있다. 그리고 열거형의 이름인 Day는 대문자나 소문자를 다르게 인식하므로 쓸때 주의하기 바란다. 다음은 complementOf( ) 라는 메소드인데 이건 괄호안의 요소들만 빼고 세트를 구성하라는 것이다. 예제에서는 es 즉 수요일과 금요일을 빼라고 하였으니 결과처럼 나머지 요일들만 출력된다. 그리고 마지막으로 range( ) 메소드를 이용하면 세트의 구간을 정해서 출력할수 있다. 어디부터 어디까지... 예제에서는 화요일부터 금요일까지 찍으라고 하였으니 화요일, 수요일, 목요일, 금요일 이렇게 구간에 해당하는 세트 요소들을 전부 출력하는 것이다. 만약 range( ) 메소드안에 반대로 금요일과 화요일 이렇게 순서를 달리해서 집어넣으면 어떻게 될까? ㅎㅎ 과연 이것도 받아들이고 정상적으로 작동할까? 이에 대한 해답은 여러분이 직접 알아내기 바란다.

EnumSet의 명령들을 접하면서 한가지 의문이 드는 이들이 있었을 것이다. 왜 객체생성을 안하징? ㅎㅎ EnumSet의 메소드들이 static으로 구성되어 있어 이렇게 빠르고 손쉽게 접근이 가능한 것이다.^^ 본 예제를 통해서 EnumSet의 진수(?)를 맛보았길 바라면서 오늘로서 Set에 관련된 클래스들은 이 정도로 마무리하고 이제부터는 Map 인터페이스에 관련된 클래스들을 다루면서 컬렉션에 대해 공부해보는 시간을 계속적으로 갖도록 하겠다.^^

2012년 11월 2일 금요일

자바의 트리셋(TreeSet)


java.util 패키지에 있는 트리셋(TreeSet)에 대해서 공부할텐데 강이의 자바강좌를 저번 시간에 들은 이들은 예고를 살짝(?) 했으니 아마도 오늘 내용에 대해서 벌써 알고 있을 것이라 생각한다. 그렇다! 요소들을 숫자든 문자든 자연적인 순서의 오름차순으로 정렬하는 것인데 그럴때 이용하는 클래스가 바로 트리셋이다.


오늘 예제는 재탕(?)을 하였다. 저번 예제를 그냥 트리셋으로만 바꿔서 어떻게 결과가 달라지는지 아주 단순히 비교할수 있도록 하였다. 여러분이 이전 강좌를 기억하리라 기대 아닌 기대(?)를 하면서 예제와 그에 따른 결과를 보도록 하자.^^


TreeSet={2,1,2,100,9,86}
ts size( ): 5
=== 강이의 JAVA강좌 ===
ts Number: 1
ts Number: 2
ts Number: 9
ts Number: 86
ts Number: 100

TreeSet2={A,B,C,A,E,D}
ts2 size( ): 5
=== 강이의 자바강좌 ===
ts2 Alphabet: A
ts2 Alphabet: B
ts2 Alphabet: C
ts2 Alphabet: D
ts2 Alphabet: E

예제 샘플은 저번이랑 똑같다. 결과에서 보다시피 트리셋 클래스도 해쉬셋처럼 중복은 허용하지 않는다. 하지만 트리셋 클래스를 이용하면 이렇게 쉽게 자연적인 순서대로의 정렬이 가능하다. 그런데 예제를 보면 필자가 HashSet을 TreeSet으로 바꾼거 이외에는 없는데 저번 예제의 해쉬셋을 건드리지 않고 그냥 해쉬셋 요소들을 트리셋에 넣을순 없을까...? 이렇게 속(?)으로 생각하고 있었다면 정말 자앙(?)하다. 잠시 라면이라도 끓여먹고 와라~ ㅎㅎ

해당 컬렉션내의 모든 요소들을 트리세트(크리쑤마쑤 트리아님.ㅎㅎ)에 넣을수 있는데 이럴때 이용하는 것이 Set 인터페이스에서 받아 구현한 addAll( )이라는 메소드이다. 예제를 기준으로 해쉬셋 요소들을 트리셋으로 옮기려면 숫자의 경우 ts.addAll(hs)를 쓰면 되고 문자의 경우 ts2.addAll(hs2)를 쓰면 된다.^^

해쉬셋을 이미 통달(?)한 여러분이기에 쉽게 기억하라는 뜻에서 예제를 저번과 같게 하였으니 오늘 내용이 머리속에 제대로 박혔으리라 기대한다.ㅎㅎ 필자가 예제에 직접 실행해보지 않은 부분에 대해서는 관련 요소들을 트리셋으로 옮겨가면서 여러분이 직접 소스를 만들어서 해보기 바란다.ㅎㅎ

그럼 여기서 한단계 더 나아가서 생각해 보기로 하겠다. 자연적인 순서(natural ordering)는 TreeSet에서 이렇게 바로 구할수 있겠지만 아예 반대로.. 거꾸로 음.. 그래 역순으로 정렬할순 없을까라는 의구심(?)이 들것이다. 이런 요소셋을 그냥 역순으로 정렬하는거 정도는 이제 손쉽게 가능해졌지만 아직까지 모르는 이들이 많을 것이다.ㅎㅎ 그것은 자바 6부터 새로나온 부분이기 때문에 관심있게 다시 보지 않았으면 아직까지 생노가다(?) 작업을 하고 있을텐데 오늘 강이의 자바강좌를 통해 이런 노역(?)에서 해방되기를 바라면서 계속해 보겠다.^^


이제부터는 자바6에서 등장한 신기술(?)을 살펴보도록 하겠다. 자바 6에서는 NavigableSet 인터페이스가 위처럼 추가되었는데 알아두면 관련 요소들을 다루는데 많은 도움이 될것이니 한번 살펴보기 바란다. 여기서는 그 중에서도 몇가지만 압축해서 예제를 선보이도록 하겠다. 빠른 이해를 위해 기존 예제를 토대로 새로운 기능을 실험하여 어려운건 없을테니 긴장풀고 편안하게 아래의 예제를 보기 바란다.ㅎㅎ


내용이 길어보이지만 절반은 기존 예제이므로 아래의 절반만 이해하면 되니 잠시 걱정은 접어두기 바라고 결과를 보면서 해설을 따라오기 바란다.^^

TreeSet={2,1,2,100,9,86}
ts size( ): 5
=== 강이의 JAVA강좌 ===
ts Number: 1
ts Number: 2
ts Number: 9
ts Number: 86
ts Number: 100
내림차순 반복자를 이용해서 거꾸로 출력
ts Number: 100
ts Number: 86
ts Number: 9
ts Number: 2
ts Number: 1
요소세트 자체를 그냥 거꾸로 출력
원본: [1, 2, 9, 86, 100]
결과: [100, 86, 9, 2, 1]
=== http://alecture.blogspot.com ===
[1, 2, 9, 86, 100]의 원본 ts에서 원하는 요소 찾기
ts.ceiling(86): 86
ts.floor(9): 9
ts.higher(86): 100
ts.lower(9): 2
ts.first( ): 1
ts.last( ): 100
=== 강이의 JAVA강좌 ===
[100, 86, 9, 2, 1]의 결과 ns에서 원하는 요소 찾기
ns.ceiling(86): 86
ns.floor(9): 9
ns.higher(86): 9
ns.lower(9): 86
ns.first( ): 100
ns.last( ): 1

여기서 우리가 보기 시작해야될 부분은 "내림차순 반복자를 이용해서 거꾸로 출력"한 곳이다. 말그대로 iterator를 통해서 출력하는데 이번엔 숫자가 역순으로 찍히는걸 볼수 있을 것이다. 이게 단 하나의 메소드만 바꿔주면 끝난다. 예제처럼 기존에 쓰던 iterator( ) 대신에 descendingIterator( ) 메소드만 넘겨주면 다른건 터치할 필요도 없이 원터치로 끝나버린다. 기가막히지 않는가? 거기다가 이것도 귀찮다 싶으면 아까전에 언급한 NavigableSet을 통해 관련셋을 바로 역순으로 정렬할수도 있다. 해당명령은 아래와 같다.
NavigableSet ns = ts.descendingSet( );


예제를 직접 실행하면서 보면 바로 이해가 갈것이다. 그밖에도 새로 추가된 ceiling, floor등의 메소드들이 있어서 어떤 것인지도 보여줄겸 살짝 예제에 추가시켜보았다. first나 last 메소드는 세트에서 가장 첫번째 요소나 마지막 요소를 찾을때 쓰는 메소드인데 이건 새로운건 아니지만 종종 쓰이니 그것도 하는김에 넣어보았다.ㅎㅎ

원하는 요소찾기에서 보면 ts랑 ns의 결과값들이 다른걸 볼수 있는데 그 차이는 어떤 세트가 기준 세트로 작동했는지에 따라서 동작이 달라지는데 그 의미를 되새겨보라고 만들어 놓은 것이다. ts는 정순이고 ns는 역순인데 그 기준 세트에 따라서 결과값이 달라지니 관심있게 살펴보기 바란다. 예제에 있는 메소드 기능들을 간략하게 설명하면 다음과 같다.

ceiling 메소드나 floor 메소드는 세트에 넣어준 값과 같은 값이 있을시 그 해당값을 리턴한다. 허나 higher 메소드나 lower 메소드는 말그대로 -er이 붙었으니 같은 값은 무시하고 그 다음에 관련된 해당값을 리턴한다. 물론 모두다 관련값이 없을시엔 null을 리턴한다. 쉽게 생각하는 방법은 세트에 넣어준 값의 위치를 기준으로 위(ceiling/higher)인지 아래(floor/lower)인지 판단하면 된다. 예를 들어 메소드에 5를 넣어준다고 가정하면 ts 세트일 경우 숫자 2 뒤에 들어간다 생각해야되고 ns 세트일 경우 역순이니까 숫자 2 앞에 들어간다 생각하고 답을 유추하면 된다. 그러면 ts 세트의 경우 9와 2가 나올것이고 ns 세트의 경우 2와 9가 나올것이다.ㅎㅎ 조금 헤깔릴지 모르겠지만 숫자 바꿔가면서 놀다보면 감이 잡힐 것이다. 이 정도만 알고 있으면 이해하는데 큰 문제는 없으리라 본다.^^

이로서 TreeSet을 공부해보는 시간을 가져보았다. 예제로 응용해 보고 싶은 부분들이 지금쯤 계속해서 생겨나고 있을것이라 생각한다. 그 욕망들(?)을 억누르지 말고 그대로 여러분의 자바머신에서 마음껏 뿜어내기 바라면서 오늘은 이만 마치겠다.^^