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

2012년 10월 31일 수요일

자바의 해쉬셋(HashSet)과 연결해쉬셋(LinkedHashSet)


중복을 용납하지 않고 순서도 제멋대로인 까칠(?)한 성격의 컬렉션인 자바의 해쉬셋(HashSet)과 그래도 넣은 순서는 기억하는 연결해쉬셋(LinkedHashSet)에 대해서 살펴보는 시간을 갖도록 하겠다.^^ 계보도는 그림대로고 해쉬셋은 이름에서 알수 있듯이 Set 인터페이스를 구현하고 있다. 자세히보면 도형들의 색깔이 다른데 인터페이스와 클래스를 구분한 것이고 맨위의 Iterable은 자바의 중심인 java.lang 팩키지에 있는 인터페이스이니 궁금하면 찾아보기 바란다.ㅎㅎ


각각의 인터페이스나 클래스가 어느 팩키지에 속해있는지 궁금해하는 이들을 위해 살펴보라고 올린 그림이다. 아님 말구..ㅎㅎ 아주 간단한 예제를 통해 어떤 경우에 해시셋을 쓸수 있는지 살펴볼 것이다. 금방 끝나니 잠시만 시선을 모아주기 바란다.^^


다른건 기억하지 못하더라도 오늘 강의에서 딱 하나는 기억하기 바란다. HashSet은 중복을 허용하지 않고 들어간 순서도 제멋대로다. 오케 버뤼~ 거기까지..ㅎㅎ

해쉬셋은 이렇게 중복을 허용하지 않고 또한 순서대로 값을 다루지도 않기 때문에 임의의 숫자나 문자열을 가지고 플레이할 대상에 쓰인다. 그 예로 로또같은 복권을 들수 있지 않을까? 이번주는 당첨되려나..ㅎㅎ

예제는 크게 각각 두가지 파트로 구성하였다. 첫번째는 해쉬셋 hs 변수를 이용해서 숫자를 넣고 출력해보는 예제이고 두번째는 해쉬셋 hs2 변수를 이용해서 문자를 넣고 출력해보는 예제이다. 그리고 또한 첫번째와 두번째에 연결해쉬셋(LinkedHashSet)을 적용했다. 쉽게 구별하라고 연결해쉬셋의 변수는 기존 해쉬셋 변수앞에다가 영문자 "l"만 추가하였으니 알아보기 쉬울 것이다.


연결해쉬셋은 위의 그림이나 이름에서 알수있듯이 Set 인터페이스를 계승함과 동시에 기존 해쉬셋을 보완하는 역활을 한다. 더 자세히 설명하자면 자바4에서 등장한 연결해쉬셋(LinkedHashSet)은 해쉬셋처럼 중복은 허용하지 않지만 해쉬셋과는 다르게 요소를 넣은 순서는 기억해서 출력한다는 점이다.ㅎㅎ 무슨 얘기인지 바로바로 캐취가 안된다면 아래의 예제를 보면 다 이해가 될것이니 이제 본격적으로 예제를 들여다 보자.^^


여러분이 컬렉션의 리스트를 통해 열공(?)해 왔을터이니 Set도 그리 어렵지 않으리라 생각한다. 예제를 실행해보면 바로 내용을 알수 있도록 구성하였으니 막힘이 없을 것이다.ㅎㅎ

첫번째 구문은 숫자가 무작위로 되어있는 셋인데 여기에 중복되는 숫자들이 있다. 이것들을 넣었을때 어떻게 해쉬셋과 연결해쉬셋이 만들어지는지 보는 것이다. 두번째 구문은 숫자 대신 문자를 넣은 예제이고 역시 중복되는 알파벳을 넣었을때 어떻게 해쉬셋과 연결해쉬셋이 만들어지는지 보라고 만들었다. size( ) 메소드는 셋에 들어있는 요소들의 갯수를 보라고 만든것인데 원래셋의 요소갯수보다 작아질 것이다. 중복되는 숫자나 문자는 셋에서 제외될테니 말이다. 여기서 셋(Set)에 들어있는 요소갯수를 우리는 영어로 cardinality라고 한다. 수학이나 데이터를 다룰때 종종 나오는 단어인데 이걸 유창(?)하게 영어로 한번더 해설하자면 # of elements of the set 되시겠다. 갑자기 생각나서 써봤다. 알아두면 써먹을데가 있을 것이다.ㅎㅎ 이제 예제의 결과를 보자.^^

HashSet and LinkedHashSet={2,1,2,100,9,86}
hs size( ): 5
lhs size( ): 5
=== 강이의 JAVA강좌 ===
hs Number: 1
hs Number: 100
hs Number: 2
hs Number: 86
hs Number: 9
=== http://alecture.blogspot.com ===
lhs Number: 2
lhs Number: 1
lhs Number: 100
lhs Number: 9
lhs Number: 86

HashSet2 and LinkedHashSet2={A,B,C,A,E,D}
hs2 size( ): 5
lhs2 size( ): 5
=== 강이의 자바강좌 ===
hs2 Alphabet: D
hs2 Alphabet: E
hs2 Alphabet: A
hs2 Alphabet: B
hs2 Alphabet: C
=== http://alecture.blogspot.com ===
lhs2 Alphabet: A
lhs2 Alphabet: B
lhs2 Alphabet: C
lhs2 Alphabet: E
lhs2 Alphabet: D

결과만 봐도 예제 내용을 알수 있도록 정성아닌 정성(?)을 들였다.ㅎㅎ 결과의 처음 출력문은 해당요소들을 차례대로 관련 해쉬셋에 넣은걸 보여주는 것이고 아니다 다를까 size( ) 메소드를 이용해 체크해보니 요소갯수가 하나씩 줄었는데 결과에서 보면 숫자 2와 알파벳 A를 넣었을때 반응이 없다는걸 볼수 있을 것이다. 해쉬셋(HashSet)과 연결해쉬셋(LinkedHashSet) 모두 중복을 허용하지 않으니까 말이다.

그럼 여기서 조금 어려운 질문을 한가지 던져보겠다.ㅎㅎ 그냥 생각만 해보기 바란다. 지금 해쉬셋에 숫자 2가 두개 들어있는데 그중 하나는 2 대신에 2.00 이 들어있다고 치자. 그럼 2와 2,00을 해쉬셋에서는 같다고 취급할까 아니면 다르다고 취급할까?ㅎㅎ 재밌게도 해쉬셋에서는 이것을 다르다고 취급하여 중복을 혀용한다. 물론 우리가 상식적인 관점에서 볼때는 말이 안되더라도 말이다.^^

해쉬셋(HashSet)에서 내부적으로 중복여부를 검사할때 기본적으로 내재되어 있는 equal( ) 메소드를 통해 요소가 같은지를 체크하는데 같으면 true, 다르면 false를 돌려준다. 그런데 2와 2.00의 경우 equal( ) 메소드는 이것을 데이터형이 다를경우 다르다고 인식하기 때문에 false를 돌려준다. 하나는 int형이고 하나는 double형이고 말이다. 그래서 중복을 허용하는 이상한 사태(?)가 벌어진다. 후에 배울 트리셋(TreeSet)에서는 내부적으로 중복여부를 검사할때 기본적으로 equal( ) 메소드 대신에 compareTo( )라는 메소드를 이용한다. 이 메소드는 값의 같은지 다른지 여부에 따라 정수 -1, 0, 1을 리턴하는데 이 경우에는 0을 돌려준다. 즉 두값이 같다는 의미의 0을 돌려준다. 따라서 중복을 허용하지 않는다.^^

방금 중복여부 검사에 대해서 언급한 부분은 대부분 이해가 되지 않을 것인데 그건 너무나 당연하다. 관련 메소드에 대해서 설명한 적이 없는데 어떻게 이해를 한단 말인가? 둘다 java.lang 패키지에 들어있어 기본적으로 필요할때 자동으로 내부에서 사용되고 있는 부분인데 갑자기 이를 짚고 넘어가는 것은 나중에 여러분의 이해를 돕기위한 포석(?)으로 삼기 위함이니 그런가보다 하고 그냥 넘어가기 바란다.ㅎㅎ

그리고 해쉬셋의 경우 값 순서들도 무작위로 출력되는데 반해 연결해쉬셋은 넣은 값 순서들이 같게 출력된다는 것을 알수 있다. 그런데 여기서 한가지 의문이 생겨나는 이들이 있을 것이다. 그럼 숫자나 알파벳을 넣은 순서가 아닌 자연적으로 커지는 그냥 일반적인 순서대로 정렬하는 방법은 없을까? ㅎㅎ 물론 있다. 그건 지면할애상 다음 시간에 하려고 한다. 좀만 기다려라.ㅎㅎ HashSet과 LinkedHashSet이 이렇게 작동된다는걸 필자가 온몸으로 보여줬으니 여러분도 성의를 생각해 답만 보지말고 직접 프로그램을 만들어서 이리저리 돌려보며 해쉬셋과 연결해쉬셋에 대해서 파고들기 바란다.^^

2012년 10월 29일 월요일

자바의 스택(Stack)


java.util 팩키지에 있는 자바의 스택(Stack)에 대해서 공부해 보기로 하자. 스텍은 벡터를 상속받고 있으며 기본적으로 5가지의 기능과 함께 벡터를 스택으로 대체해 쓰는게 가능하다. 스택은 LIFO(Last-In-First-Out)로 표시하곤 하는데 그림에서 보다시피 나중에 들어간 아이템이 가장 먼저 나온다는 원리이다.


스택은 방금 언급했다시피 총 5가지의 메소드를 지원한다. 몇개 안되니 아래에 써보기로 하겠다.

boolean empty( )
int search(Object o)
E push( E item)
E pop( )
E peek( )

위의 기능들을 한문장으로 정리하자면 비어있는지 확인하고 안에 특정 요소가 있는지 살펴보고 요소를 넣고 빼고 뭐가 있는지 살짝 보는게 위의 5가지 메소드들이 하는 일이다.^^ 예제를 보면서 어떻게 작동되는지 실험해 보기로 하자.


오늘 코딩할 예제는 위와 같다. 기본적으로 5개의 요소들을 집어넣은 상태에서 pop( ) 메소드를 이용해 가장 나중에 넣은 요소 12를 꺼내고 다음에 새로운 요소 83을 넣는 예제이다. 그림만 봐도 이해가 될것이다. 스택을 사용할때는 위와같은 형태의 구조를 떠올리면 쓰기 쉬울 것이다.^^ 그럼 예제 코드를 보기로 하자.


true
false
[17, 5, 123, 25, 12]
=== 강이의 JAVA강좌 ===
12
[17, 5, 123, 25]
[17, 5, 123, 25, 83]
4
=== 강이의 자바강좌 ===

스택의 5가지 기능을 모두 넣어 예제를 구성하였으니 본 예제 하나로 스택의 기능들이 어떤게 있는지 다 알아볼수 있을 것이다.ㅎㅎ 많은 이들이 스택에 관련된 질문중에서 자주 틀리는 부분이 한가지 있다. 오늘 그 중요한 맥을 필자가 짚어줄 것이니 끝까지 예제에 집중해주기 바란다.^^

예제를 보면 그림처럼 스택에다가 관련 숫자들 5개를 for 구문을 통해 처음에 넣는다. 넣기전에 empty( ) 메소드를 이용해서 스택이 비어있는지 물어보니까 비어있어서 true라고 찍힌 것이다. 숫자들을 다 넣은후에 다시 물어봤더니 스택이 이제 차 있으니까 false라고 찍혔다. 그리고 관련 요소들을 출력하라고 하니 스택에 있는 요소들을 쭈욱 찍은 것이다. 화면상에는 결과에서처럼 옆으로 나열되어서 숫자들이 찍히지만 여러분은 위의 예제 그림처럼 숫자들이 들어있다고 생각하기 바란다.^^

그 다음에 peek( ) 메소드를 이용해 가장 위에 있는 요소를 살짝 엿보고 찍으라고만 하니 12가 찍힌 것이고 다음에 pop( ) 메소드를 실행하였다. 이 메소드가 하는 일은 스택에서 맨 상위 요소를 하나 꺼내는 것이다. 그래서 요소 12가 제거되고 난 후의 요소들 4개가 찍힌 것이고 push( ) 메소드를 이용해 83을 넣으니 스택에 총 5개의 요소들이 찍혔다.ㅎㅎ 여기까지는 여러분이 생각하는 그대로일거다. 오늘의 하이라이트인 마지막 출력 부분에 4라는 숫자가 찍혔다. 이 부분에서 막히는 이들이 많을 것인데 스택에서 가장 헤깔려하는 부분이므로 확실히 알고 넘어가기 바란다.ㅎㅎ

search( ) 메소드를 이용해서 관련요소 5를 찾으라고 했는데 아마 대다수가 1이 찍힐거라고 예상했을 것이다. 그것도 아니라면 2가 찍혀야 정답의 근사치가 아닐까 생각할텐데 이런 부분 때문에 특히 스택을 예제의 그림처럼 항시 생각해야 편한 것이다. 스택에서는 해당 요소를 찾으라고 검색할 경우 스택의 위쪽에서부터 카운트를 한다. 카운트도 가장 최상위의 아이템을 1로 잡고 카운트한다. 따라서 우리가 찾는 요소 5 위에 세개의 요소가 더 있으니 맨 위를 1로 잡고 아래로 세어 나가다보면 요소 5가 있는 위치가 4가 됨을 알수 있을 것이다. 이렇게 1부터 기본적으로 세어나간다고 해서 이를 1-based position이라고 표현한다.^^

자바뿐만 아니라 Data Structure에 관계된 프로그래밍을 공부할때 항상 빠지지 않고 등장하는 것중에 하나가 스택이니 어떤 것인지 머리속에 정리해 두기 바라면서 스택 강의는 여기서 마치도록 하겠다.^^

2012년 10월 28일 일요일

자바의 연결리스트(LinkedList)


오늘은 컬렉션 중에서도 자바의 연결리스트인 LinkedList에 대해서 공부하는 시간을 가져보기로 하겠다. 연결리스트 즉 링크리스트(LinkedList)는 큐(Queue)의 기능을 활용한 클래스 중에서 가장 많이 쓰이는 클래스로서 리스트(List)의 기능도 구현하고 있으므로 다목적으로 사용이 가능하다. 컬렉션에서 ArrayList가 LinkedList에 비해서 훨씬 많이 쓰이긴 하나 프로그램에서 요소들의 잦은 삽입이나 삭제를 반복하는 경우가 빈번하게 발생한다면 프로그램의 효율성을 위해 연결리스트를 사용하는 것에 대해 고려해볼만 하다.^^


위의 그림은 LinkedList의 구조에 대해서 표현한 것이다. Queue에 대해서 조금 아는 이들이라면 쉽게 이해가 될것인데 설명하자면 연결리스트의 head 부분은 당연히 첫번째 요소를 뜻하는 것이고 tail은 맨 마지막 요소를 뜻한다. 각 요소가 다음 요소와 링크로 연결되어 있으며 맨 마지막 요소의 링크는 그림처럼 아무것도 없는 허공(?null)을 지정하고 있다고 생각하면 이해가 쉬울 것이다.^^

이렇게 링크로 연결되어 있는 연결리스트가 잦은 삽입시 배열리스트에 비해서 왜 효율적인지 지금부터 설명하겠다. 배열리스트의 경우에 요소를 삽입한다면 삽입한 그 자리 이후에 위치한 요소들은 전부 뒤로 한칸씩 밀어야한다. 삽입하는 자리가 거의 뒤쪽이라면 별 지장이 없겠지만 요소들이 많은데 거의 처음에 요소를 넣어야하는 상황이 발생할 경우 시스템에 엄청나게 부하(?)가 걸릴수 있다. 그건 요소를 삭제하는 경우에도 마찬가지일 것이다. 그때는 반대로 그 뒤의 요소들을 모두 한칸씩 앞으로 보내야하니까 말이다.ㅎㅎ 그에 반해 연결리스트를 사용할 경우 요소를 넣을 위치에 다음 요소를 가리키는 링크의 위치만 변경하면 다른 부가작업이 필요가 없다. 말로 표현하는 것보다 그림을 보면 훨씬 간단하니 아래를 보기 바란다.^^


37이라는 새로운 요소를 12와 99 사이에 넣으려는 예제인데 그림에서 보는 것처럼 12의 링크를 새로 넣으려는 요소 37에 지정하고 새로운 요소 37의 링크는 12 다음에 있던 요소 99로 지정하면 요소의 삽입과정이 간단하게 끝난다.


위는 반대로 중간에 있는 요소인 99를 삭제하는 과정이다. 이건 그림에서 보는 것처럼 단지 이전의 요소가 가리키고 있던 링크를 삭제하려는 요소를 건너뛰어 다음의 요소에 링크만 걸어주면 삭제과정이 끝난다. 물론 나머지 링크는 그대로 유지된다.^^

보다시피 연결리스트가 이러하므로 수시로 요소들을 추가거나 삭제한다면 연결리스트가 훨(?) 나을수도 있다. 그럼 연결리스트의 작동원리는 이 정도로 하고 이제 예제를 보기로 하자. 이번 예제는 Queue에 포커스를 맞추어서 내용을 구성하였다. 어차피 이전강의들에서 리스트에 관계된 대부분의 메소드들은 공부하였으므로 큐에 대해서 건드려 보도록 하겠다. 연결리스트(LinkedList)가 큐(Queue)를 구현한 대표적인 클래스이니까 말이다.^^


예제는 아주 심플하면서도 심오(?)하다. 이런걸 한마디로 표현하자면 누워서 떡먹기인데 결과는 아래와 같다.^^

[A]
[A, B]
[A, B, C]
[A, B, C, D]
[A, B, C, D, E]
=== 강이의 JAVA강좌 ===
[A, B, C, D, E, A]
B
[B, C, D, E, A, B]
C
[C, D, E, A, B, C]
D
[D, E, A, B, C, D]
E
[E, A, B, C, D, E]
A
=== 강이의 자바강좌 ===
[A, B, C, D, E]
[A, B, C, D, E]

특별히 어려운건 없을 것이나 위아래 표식이 있는 강이의 자바강좌 사이에 있는 결과값들에 의문을 표하는 이들이 조금 있을지도 몰라 혹시나 정말 혹시나 해서 해석을 곁들이니 이해가 잘 안갔던 이들은 함께 하기 바란다.ㅎㅎ

일단 연결리스트에 A, B, C, D, E 알파벳을 차례대로 넣어서 연결리스트를 만들었다. 초반에 언급했지만 연결리스트 또한 리스트를 계승하므로 add( ) 메소드를 자유롭게 쓸수 있다. 그럼 오늘 예제의 하이라이트(?)인 두번째 for 구문을 살펴보자.

일단 아까처럼 요소 A를 add( ) 메소드를 통해 집어넣으니 결과처럼 연결리스트 맨 뒤에 A가 추가된다. 다음에 어째서 B가 출력되느냐인데 메소드가 하는 기능을 제대로 알면 금새 이해가 될것이다. poll( ) 메소드를 썼는데 이것의 뜻이 개표 또는 여론조사할때 쓰는 표현이다. 개표라는 말의 의미자체가 투표함에서 표를 꺼내서 열어본다는 것이니 연결리스트가 투표함이고 표가 요소라고 가정할 경우 요소를 꺼내서 열어보니 당연히 연결리스트에서는 더이상 꺼낸 그 표, 즉 요소가 없을 것이다. 그런데 poll( )의 메소드는 연결리스트 맨 앞의 요소 헤드, 즉 맨 처음 넣었던 요소를 끄집어내고 그 요소는 없애버린다. 따라서 poll( ) 메소드를 실행한 후의 연결리스트는 [B, C, D, E, A]가 될것이다.ㅎㅎ

그 다음 peek( ) 메소드를 실행하고 출력하라고 했는데 peek의 뜻이 무언가를 엿보다 혹은 훔쳐보다라는 뜻이다. 즉 보기는 보지만 말그대로 그 요소를 삭제하거나 하는 행동은 취하지 않는다. peek( ) 메소드도 poll( )과 마찬가지로 맨 앞의 head 요소를 사용한다. 현재 연결리스트가 A를 끄집어낸 후이므로 가장 앞의 위치한 요소 B를 출력하게 되는 것이다. 이런 식으로 계속해서 반복되는게 두번째 for 구문이다.^^

마지막으로 clone( ) 메소드로 연결리스트를 복사하는 것인데 clone( ) 메소드가 돌려주는 리턴값이 Object라서 연결리스트로 바꿔주느라 캐스팅을 사용한 것이다. 물론 예제처럼 연결리스트로 복사해서 다시 쓸 필요없을 경우 원래의 리턴값인 Object로 받아서 써도 된다. 그렇게 하려면 주석처리한 부분으로 현재의 코드를 수정하면 될것이다.ㅎㅎ

짧게나마 연결리스트(LinkedList)에 대해서 공부해보는 시간을 가져보았다. 이밖에 여러 기능들이 있지만 그건 여러분이 조금만 응용해서 코딩해보면 쉽사리 습득하게 될것이다. 자바 라이브러리(API)는 자바의 바이블같은 존재이다. 내용이 방대하므로 전부다 실험은 못해보더라도 최소한 의문이 생기는 부분들에 대해서만이라도 여러분이 찾아보고 알아보는 정성을 쏟기 바라면서 오늘 강의는 이정도로 하겠다.^^

2012년 10월 26일 금요일

자바 컬렉션 프레임워크(Java Collections Framework)

오늘은 강이의 자바강좌에서 여러분과 약속한데로 자바 컬렉션 프레임워크에 대해 특강 형식으로 강의를 진행하려한다. 세계최초(?)로 공개되는 본 강좌를 들으면서 자바의 컬렉션에 대한 여러분의 궁금증들을 해소하고 그에 관한 폭넓은 사고와 이해를 통해 여러분의 지식을 한층 드높이는 계기가 되리라 단언한다. 특강이니만큼 자바소스나 프로그램은 제외할 것이고 읽는 도중에 아직 배우지 못한 내용들은 앞으로 진행될 강의에서 자세히 설명할 예정이니 그런게 있구나하고 편안하게 글을 읽어내려가기 바란다. 강이의 자바강좌에서 오늘 특강을 애독하는 이들은 아마 자바 컬렉션 프레임워크에 대한 전반적인 개요와 구조를 확실하게 이해하는데 큰 도움을 줄것이라 확신한다. 필자가 오늘 강의를 준비하기위해 도봉산과 관악산의 정기를 받으며 수개월동안 피와 땀이 맺힌 정신수양(?)을 한끝에 드디어 본 특강을 세상에 내놓으니 졸지말고 끝까지 함께해 주기를 희망하면서 본론으로 들어가보겠다.ㅎㅎ


자바 컬렉션 프레임워크(Java Collections Framework) 일명 JCF는 다수의 데이터와 객체지향적 시스템을 기반으로한 계층적 구조로서 여러 문제들을 풀기위해 서로 관련성이 있는 객체들을 통일하고 표준화된 설계들을 통해 유기적 관계들을 그룹화시키고 정형화시켜 보다 구체화된 다수의 데이터를 쉽게 처리할수 있는 방법을 제공하는 클래스들과 재사용할수 있는 컬렉션 데이터 구조들을 구현하는 인터페이스들의 집합을 말한다. 자바의 다형성을 이용해서 컬렉션 객체들의 메소드 형식과 형태를 동일하게 유지시켜 누구나 편하게 사용할수 있도록 일관성있게 제작된 자바 컬렉션 프레임워크는 자바2(JDK 1.2) 버젼에서 등장하여 많은 개발자들에게 자바 배열과 이와 관련된 여러 불편한 사항들과 단점들을 궁극적으로 보완할 최선의 대안책으로 제시되며 현재에 이르기까지 높은 성능과 뛰어난 상호운용성으로 크게 각광받고 있다. 거기다 기존의 1.4 버젼까지는 자료구조가 전부 Object를 넣어서 쓸수있도록 프레임워크가 설계되었으나 1.5인 자바5부터 제네릭을 지원하게 됨에따라 데이터형을 사전에 원하는데로 지정할수 있어 더욱더 편리한 사용이 가능해졌다.^^

지금부터는 자바 컬렉션 프레임워크를 일반적으로 지칭하는 컬렉션이란 용어로 통일하여 사용하면서 이에 대해 심층분석해 보기로 하겠다. 컬렉션은 간단하게 말해 배열에 관련된 데이터를 다루는 인터페이스들과 이를 구현한 클래스들의 집합이라 할수 있을텐데 개발자 입장에서는 어떤 인터페이스들과 클래스들이 있는지 알아야할 것이고 이것이 어떻게 쓰이는지 기본이라도 알아야 써먹을수 있을 것이다. 따라서 이제부터는 여러분의 지루함도 달래줄겸 특별히 그림도 같이 보아가면서 공부해 보기로 하겠다.ㅎㅎ


자바 컬렉션의 핵심 인터페이스들이다. 관련 클래스들은 어차피 인터페이스들을 알면 부수적으로 따라오는 것이므로 컬렉션에 대해서 논할려면 최소한 위의 계층구조 정도는 파악하고 있어야한다. 자바의 콜렉션 프레임워크는 크게 두개의 인터페이스(Collection과 Map)로 나뉘어진다. 보다시피 컬렉션의 뿌리인 Collection 인터페이스를 기반으로 이를 계승한 Set, List, Queue(자바5에서 등장)의 계층구조로 펼쳐지며 Collection 인터페이스를 계승하지 않고 독자적으로 존재하는 Map 인터페이스는 방식이 다르지만 성격은 같으므로 컬렉션에 속한다. 참고로 Map 인터페이스의 방식은 다른 컬렉션 인터페이스들과 다르게 키(Key)와 값(Value)을 이용 객체의 쌍(pair)으로 데이터를 짝지어 모아둔다. 여러분이 위의 인터페이스들만 다 이해하여도 자바 컬렉션 프레임워크의 대부분을 다 알고 있다고 해도 결코 과언이 아닐 것이니 다음 단락으로 가기전에 다시한번 위의 그림을 쳐다보고 아래로 이동하기 바란다.^^

컬렉션은 대부분이 객체를 어떻게 분류해 모아놓을지 다양한 연산을 수행하는 알고리즘으로 구성되어 있다고 볼수 있으며 구체적으로 표현해 보자면 컬렉션에서 원하는 객체를 찾는 Search 알고리즘과 객체를 특정한 순서대로 정렬하는 Sort 알고리즘을 중심으로 그 내용이 구성되어 있다고 간략하게 말할수 있겠다.ㅎㅎ

그럼 이제 각 컬렉션들에 대한 내용들을 요점정리해 보는 시간을 가져보겠다. 일단 컬렉션에서 맨상위 뿌리가 되는 Collection 인터페이스는 이를 계승하는 하위 인터페이스들에게 다음과 같은 메소드들을 지원한다.

 boolean add(E e)
 boolean addAll(Collection<? extends E> c)
 void clear( )
 boolean contains(Object o)
 boolean containsAll(Collection<?> c)
 boolean equals(Object o)
 int hashCode( )
 boolean isEmpty( )
 Iterator<E> iterator( )
 boolean remove(Object o)
 boolean removeAll(Collection<?> c)
 boolean retainAll(Collection<?> c)
 int size( )
 Object[ ] toArray( )
 <T> T[ ] toArray(T[ ] a)

외우라고 쓴것은 아니니 그냥 자연스럽게 눈이 가는데로 놓아두기 바란다.^^ 어차피 관련 클래스들을 공부하면서 자연스럽게 쓰게 되므로 저절로 알게 된다. 더군다나 여러분은 벌써 컬렉션 List에 해당하는 Vector와 ArrayList 클래스에서 벌써 일부 메소드들을 써보았을 것이므로 설령 생소한 메소드가 보여도 비슷한 맥락이므로 그다지 어렵진 없을 것이다.ㅎㅎ 그럼 컬렉션을 구성하고 있는 하위 인터페이스들의 특징과 그와 관련된 대표적인 클래스들을 함께 살펴보기로 하자.^^ 필자가 식음(食飮)을 전폐(全廢)하며 여러분이 한방(?)에 이해할수 있도록 최대한 핵심만 간추려 보았다.ㅎㅎ

- 자바 컬렉션 프레임워크 Java Collections Framework 구성요소들 -
Set: 요소들의 중복을 허용하지 않고 순서를 가지지 않는 요소들의 집합
Set 인터페이스를 구현해 가장 많이 쓰이는 대표적인 클래스: HashSet
SortedSet: 요소들을 순서대로 꺼내 읽을수 있고 요소들이 증가되는 순서를 가지는 집합
SortedSet 인터페이스를 구현해 가장 많이 쓰이는 대표적인 클래스: TreeSet
List: 요소들의 중복을 허용하고 인덱스를 이용해 순서를 가지는 요소들의 집합으로 시퀀스(sequence)라고도 표현한다.
List 인터페이스를 구현해 가장 많이 쓰이는 대표적인 클래스: ArrayList
Queue: FIFO(First-In-First-Out)의 형태로 첫번째로 넣은 요소가 첫번째로 나오는 방식
Queue 인터페이스를 구현해 가장 많이 쓰이는 대표적인 클래스: LinkedList

Map: 키(Key)와 값(Value)이 쌍(pair)으로 즉 두개의 객체로 이루어져 있으며 키는 중복을 허용하지 않고 값은 중복을 허용하는 순서를 가지지 않는 요소들의 집합
Map 인터페이스를 구현해 가장 많이 쓰이는 대표적인 클래스: HashMap
SortedMap: 키에 해당하는 객체가 증가되는 순서대로 나열한 요소들의 집합
SortedMap 인터페이스를 구현해 가장 많이 쓰이는 대표적인 클래스: TreeMap

간략하게나마 자바의 컬렉션 프레임워크를 구성하고 있는 핵심 인터페이스들의 특징들을 살펴보았다. 어차피 처음보는 것이라면 이해하지 못해도 전혀 상관없다. 관련 강좌들을 차례차례로 듣다보면 퍼즐 맞추듯이 하나하나 깨닫게 될것이다. 조각이 어느정도 맞춰진 후에 본 특강을 다시 읽는다면 머리속이 깨끗하게 정리가 될것이니 오늘은 부담없이 가볍게 넘어가기 바란다. 위의 인터페이스들과 이를 구현한 클래스들의 연관관계를 아래 그림들을 통해 순차적으로 감상하기 바란다.^^





그림들을 순차적으로 보다보면 자바 컬렉션 프레임워크가 어떤 인터페이스들과 어떤 클래스들로 구성되어 있는지 눈에 들어오기 시작할 것이다. 마지막 그림은 자바4와 자바5 버젼에서 어떤 것들이 컬렉션에 추가되었는지 살펴보라고 올린 그림이다. 물론 이것 이외에도 더많은 인터페이스와 클래스가 컬렉션에 관계되어 있다. 그중에서도 자주 쓰이는 것들만 모아놓은 것이라 믿도록 하자.ㅎㅎ 그림으로 다 때운다고 필자를 원망하지 말아라. 때론 그림들로 커버가 가능할때가 있는데 바로 지금이 그런 경우라 하겠다.ㅎㅎ

예상보다 특강이 길어졌는데 그동안 List에 속해있는 Vector와 ArrayList를 접하면서 컬렉션에 대해 누적되어왔던 여러분의 궁금증들이 이번 특강을 통해 많이 해소되었길 바란다. 앞으로 펼쳐질 강의들은 컬렉션에 관계된 중요 클래스들을 하나하나 접하면서 여러분들의 프로그래밍 실력을 한단계 드높여줄 컬렉션의 세계로 안내할 것이다. 여러분의 끊임없는 성원을 기대하면서 오늘 특강은 여기서 마치도록 하겠다.^^

2012년 10월 22일 월요일

자바의 반복자(Iterator)와 리스트반복자(ListIterator)


오늘은 컬렉션에 관계된 자바의 반복자인 Iterator와 리스트반복자인 ListIterator에 대해서 들여다보는 시간을 갖도록 하겠다. 자바의 벡터 시간 이후로 컬렉션이란 말을 간간히 쓰고 있는데 대충 이전에 간략하게 언급은 했지만 사실 이것에 대해서 구체적인 개념이 정립되어 있지 않은 이들은 컬렉션이란 말만 나와도 머리속이 어지러운 공황상태(?)로 돌입하고 있을지도 모르겠다.ㅎㅎ 그러나 염려놓기 바란다. 어느정도 궁금증이 증폭되었을때 관련 강의를 듣게되면 그것이 머리속으로 솔솔찮게 들어올 것이니 말이다. 다음 강좌에서 자바의 컬렉션에 대한 특강을 할것인데 아마도 그것을 듣게되면 그동안 쌓였던 궁금증과 의문점들이 눈녹듯이 한순간에 사라져버릴 것이다.ㅎㅎ 여러분의 많은 기대 부탁드리면서 다음 강의 광고는 잠시 접도록 하고 다시 본론으로 들어가보기로 하겠다.^^


java.util 팩키지에 들어있는 인터페이스 Iterator와 ListIterator를 공부해볼텐데 컬렉션이랑 관계되어 있으므로 배열을 떠올릴수 있을 것이고 반복이란 의미를 생각한다면 또한 자주 이용되고 계속 반복해서 뭔가(요소들)를 돌릴수 있구나라고 어거지(?)로 유추를 해보자.ㅎㅎ 또한 이름에서 보듯이 서로 연관성이 있음을 알수 있다. 어떻게 아냐고? Iterator란 단어가 둘다 들어가 잊지 않은가? ㅎㅎ 별거 아니라고 넘기는 이들이 많겠지만 자바의 모든 팩키지들, 인터페이스들, 클래스들, 메소드들, 기타등등,, 이름만 관심있게 보아도 반은 먹고 들어갈수 있다. 자바는 이름을 지을때도 기능과 연관이 있도록 심혈을 기울여만든 프로그래밍의 걸작(?)이기 때문에 그러하다.^^

Iterator는 개체의 요소들을 출력할때 기존의 인덱스 증감인자에 의존하던 방식을 탈피해서 보다 편리하고 쉽게 출력이 가능하도록 만들어준다. Iterator는 자바 컬렉션 내부에서 자유롭게 쓸수 있으며 기존의 Enumeration의 기능을 완전히 대체함과 동시에 remove( ) 메소드를 장착하고 메소드명들도 줄여서 편리하게 쓸수 있도록 개선되었다. 따라서 구식인 Enumeration을 쓸 필요가 전혀 없다. 필자가 벡터 강의할때 잠시 어떻게 쓰는지 보여줬을텐데 이 시간 이후부터는 더 향상된 기능인 Iterator만 쓰기로 하니 여러분도 Enumeration은 깨끗이 잊어주기 바란다. 헐~ 그럼 처음부터 Iterator만 설명하지 왜 Enumeration을 보여줘서 혼란을 가중시키냐고 딴지거는 이들은 듣기바란다. 그럼 Iterator 나오기 이전에 작성된 프로그램들을 보았을때 이해는 할수 있어야하지 않겠느냐? 답이 됐나? ^^ 그럼 됐다.ㅎㅎㅎ

여러분에 대한 사랑(?)이 깊어지는 관계로 자꾸 서론이 길어지고 있다. 이제 정신을 가다듬고 Iterator가 어떤 메소드들을 가지고 있는지 살펴보기로 하자.^^

boolean hasNext( )
E next( )
void remove( )

위의 딱 3가지 메소드 밖에 가지고 있지 않다. 따라서 여러분이 이 세가지 메소드들의 기능만 쓸줄 알면 Iterator의 종결자(?)로 바로 등극할수 있으니 더도말고 3분만 여기에 시간을 투자하기 바란다.ㅎㅎ 뭘 기대하는거냐? 여기에 대해서 부연설명은 하지 않겠다. 여러분이 강이의 자바강좌를 열공했다면 이 정도쯤이야 메소드 구조만 딱 봐도 탁탁~ 아닌감? ㅎㅎ
그래도 배려심(?)이 깊다리깊은 필자가 한마디 하자면 hasNext( ) 메소드를 보면 뭔가 다음꺼를 찾는다는 것이고 리턴타입을 보니 boolean이란다. 그럼 자동적으로 리턴값이 true나 false를 돌려준다는 것이니 if나 while문 같은 컨디션에 쓰기 딱 좋다는 것을 알아차릴수 있다.ㅎㅎ next( ) 메소드야 보자마자 다음껀데 리턴타입에 E가 떡하니 붙어있으니 어떤 데이터형인지는 모르겠지만 element값을 돌려주는구나 하고 생각하면 되고 remove( ) 메소드는 말그대로 없애는 기능이구나 생각하면 되시겠다. 한마디가 너무 길었나? ㅎㅎ

이제 ListIterator를 살펴보자. ListIterator는 상위계층의 Iterator를 그대로 계승한다. 따라서 Iterator의 기능은 기본적으로 갖추고 있으며 Iterator와 크게 다른점은 양방향으로 리스트를 돌면서 관련 요소들을 출력할수 있다는 것이다. 관련 메소드들은 다음과 같은 것들이 있다.

boolean hasNext( )
E next( )
void remove( )
int nextIndex( )
boolean hasPrevious( )
E previous( )
void add(E e)
int previousIndex( )
void set(E e)

아까 필자가 한것처럼 메소드들이 어떤 기능을 가지고 있는지 탁탁~ 보기 바란다. 대충 어떤 기능들이 있는지 쉽게 눈에 들어올 것이라 믿겠다.^^ 리스트반복자는 기존 반복자에 비해서 어떻게 동작하는지에 대한 구조정도는 머릿속으로 그릴수 있어야 하는데 그 이유는 양방향으로 움직이기 때문에 상황에 따라 앞으로 갔다 뒤로 갔다거리면 자칫 무한루프에 빠질수 있기 때문이다. 여러분의 이해를 돕기 위해 특별히 그림으로 설명하겠다.^^


기존의 배열이라면 인덱스가 그냥 관련 요소를 지칭하지만 반복자에서는 위처럼 커서가 요소(Element)를 지칭하는게 아니라 요소들의 사이 사이를 지칭한다고 생각하여야 정확하다. 특히 이 개념은 리스트반복자(ListIterator)를 사용할때 더욱 중요한 개념으로 next( ) 메소드와 previous( ) 메소드를 연속해서 사용할시 기존에 쓰던 배열에서의 인덱스를 생각한다면 서로 다른 요소를 넘겨받는다고 생각하겠지만 위의 그림과 같은 인덱스 방식으로는 같은 요소를 주고받게된다. 이해가 안되면 아래 그림을 보면서 생각해보면 금새 알게 될것이다.^^


이제 이해가 되는가? 지금 위의 그림은 반복자가 next( ) 메소드를 이용해 옆으로(?) 이동했을때 그 사이에 있는 요소를 돌려준다는 것을 그림으로 표현하고 있는 것이다. 많은 이들이 반복자를 사용하고 있지만 그 원리를 제대로 모르고서 그냥 무작정 사용하고 있는 이들이 많은데 그럴경우 원치않는 요소가 가끔 튀어나올 것이다. 그건 여러분이 반복자가 어떻게 작동하고 있는지 제대로 알고 있지 못하기에 그런 상황을 겪게 되는데 강이의 자바강좌를 필독하는 이들은 이 정도의 레벨(?)은 바로 뛰어넘어 다른 미지의 세계로 나아가야할 것이다.ㅎㅎ

반복자를 공부하면서 혹시 이런 생각을 하는 이들이 있을지도 모르겠다. 아 그냥 루프는 기존의 for 룹으로 전부 다 통일하는게 오히려 더 편하지 않을까? 이런 신통방통(?)한 질문을 던지는 이들이 있을텐데 필자는 이런 여러분에게 박수를 치고 싶으나 이 사실을 알고나면 왜 반복자가 그렇게 많이 쓰이는지 알게 될것이다. 뭘까? ㅎㅎ 예를 들어 기존 배열에서 쓰는 for 룹으로 리스트를 출력한다고 치자. 그럼 이 for 룹은 인덱스 중심으로 읽히므로 리스트가 수정이 된다면 for 구문도 그에 맞춰서 같이 수정되야한다. 리스트의 길이가 어떻게 바뀔지 모르니까 말이다. 허나 반복자를 이용하면 리스트가 수정이 가해지든 않든 상관없이 최종 상태의 리스트를 넘겨받아 작동되므로 반복자를 이용한 루프 구문은 손댈 필요가 없게 되니 더 편하다.^^

물론 확장된 enhanced for 루프(for-each)도 메카니즘이 반복자를 이용하는 것이기 때문에 마찬가지다.ㅎㅎ 그럼 또 이런 질문을 하는 이들이 있을 것이다. for-each 루프문을 쓰지 왜 Iterator를 이용하는냐고 말이다.ㅎㅎ 여러분이 리스트에 변형을 가하지 않고 단순히 리스트에서 요소들을 처음부터 끝까지 출력만 하는 경우에는 Java 5에서 새로 등장한 for-each 루프문을 쓰는게 최고라고 할수 있겠다. 왜냐하면 Iterator의 경우 hasNext( ) 메소드를 이용해서 다음 요소가 있는지 확인을 해야하는데 확장된 for 루프문은 이런 과정을 우리가 수동으로 체크하지 않아도 되도록 만들어놨기 때문이다. 이번 강의의 제목이 반복자여서 반복자를 이용해 출력한 것이지 만약 다른 곳에서 단지 리스트를 출력하기만 하면 되는 경우라면 당근 자바5 신무기(?)로 무장된 enhanced for 루프문을 쓰는게 현명할 것이다.^^

이제 반복자에 대한 설명은 어느정도 정리된것 같으니 필자의 특화된 예제를 통해 Iterator와 ListIterator의 핵심동작원리를 깨우치기 바라면서 오늘의 강좌를 마치도록 하겠다. 본 예제는 반복자를 이용해 어떻게 요소들을 출력하는지 가장 기본적이면서도 빈번하게 쓰이는 방식이니 잘보고 앞으로 많이 써먹기 바란다.^^


오늘 예제는 아주 뜻깊은 예제라 할수 있겠다.- 필자가 강이의 자바강좌 예제 최초로 빨간줄을 도입한 혁신의 날(?)이기 때문이다. 그 전에 한적이 있었나? 그럼 말구~ㅎㅎ 어쨌거나 이번 예제에 빨간줄 치느라 무지 애먹었는데 그에 따른 피땀어린 노력(?)이 바래지지 않도록 더더욱 열심히 공부해주기 바란다.ㅎㅎ

예제에 있는 빨간박스 3개만 집중해서 보면된다. 나머지는 다 아는 내용일테고 첫번째 박스는 Iterator를 이용해 리스트를 출력하는 예제이고 두번째 박스는 ListIterator를 이용해 리스트를 출력하는 예제이고 마지막 세번째 박스는 ListIterator를 이용해 리스트를 거꾸로 출력하는 예제이다. 마지막 박스야말로 ListIterator가 Iterator와는 다르게 할수 있는 큰일(?)이라 할수 있겠다. 예제를 보면 그냥 이해가 될것이니 특별한 부연설명은 필요없겠지만 굳이 하나를 들춰보자면 al.iterator( )와 al.listIterator( ) 정도일꺼다. 필자가 제대로 궁금한거 찍었남? Arraylist의 al은 인터페이스 List와 관련되어 있다. 이 인터페이스 List를 구현한 클래스 중에 하나가 Arraylist인데 그 안에 위와 같이 반복자가 돌아다닐수 있는 Iterator나 ListIterator 형식으로 리스트를 받을수 있게 해주는 메소드가 들어있어서 기존의 리스트를 이렇게 그대로 쉽게 넘겨받을수 있는 것이다.^^ 이것 빼고는 여러분이 프로그램 돌려보면 별 어려운 사항이 없을테니 필자는 예제 결과값을 공개하면서 이번 강의를 마치도록 하겠다. 아래를 보기 전에 예제에 대한 결과값을 미리 예상해보고 여러분의 답과 맞춰보면서 공부하는 센스(?)장이가 되기를 희망하면서 물러가겠다.^^

ArrayList(itr): A B C D E
=== 강이의 JAVA강좌 ===
ListIterator(forward): A B C D E
=== http://alecture.blogspot.com ===
ListIterator(backwards): E D C B A

2012년 10월 21일 일요일

자바의 배열리스트(ArrayList)




자바 컬렉션 프레임워크(Java Collections Framework)에서 ArrayList 클래스는 실무에서 HashMap과 더불어 가장 빈번하게 쓰이는 클래스 중 하나이므로 이번 단원에서 어떤 관점으로 ArrayList를 강의할지 필자가 상당히 고뇌(?)하였음을 미리 알리는 바이다.ㅎㅎ 동기화(synchronized)가 Vector는 지원되고 ArrayList는 지원되지 않는 차이점을 빼고는 거의 대부분 같다고 볼수 있다. 물론 ArrayList에서도 Collections.synchronizedList라는 메소드를 이용해 List를 wrapped해서 보완해쓰는 방법이나 자바 5에서 등장한 CopyOnWriteArrayList를 쓰는 방법도 있겠지만 여러분이 멀티쓰레드 환경을 고려할 필요가 없다면 그냥 ArrayList를 쓰기 바란다.(동기화 부분에 대해서는 이미 강의한바 있으니 잘 기억이 나지 않는 이들은 예전 강의를 참조하기 바란다.) 메소드의 경우에도 벡터와 태반이 비슷하며 사소한 몇가지는 여러분이 자바 라이브러리를 보면서 필요할때 쓰면 되므로 이미 저번 강의에서 Vector를 어느정도 마스터(?)한 여러분이기에 이번 강의에서는 저번과 궤를 달리하겠다. 그래도 되겠지? ㅎㅎ

오늘은 여러분의 프로그래밍 기술을 한단계 업그레이드시켜주는 관점에서 본 강좌를 진행하려고 하니 주변에 모든것을 잠시 미루고 ArrayList 강의에 총력을 다해주기 바라는 바이다.^^ ArrayList는 이미 언급한데로 Vector와 유사하므로 역시 자동으로 배열 길이를 조정할수 있으며 리스트가 찰때 알아서 길이를 조정해주므로 여러면에서 편리하다. 그렇다면 동기화를 고려할 필요가 없을경우 둘다 써도 되는 상황일때 ArrayList를 쓰는게 나은건지 Vector를 써야되는게 좋은지 고민하는 이들이 상당수 있을 것이다. 물론 상황이나 속도등 효율성에 따라 다르겠지만 정답은 없다. 그 상황에서 여러분에게 가장 적합한 방식을 쓰면되고 어떤 방식으로 구현해도 무리가 없다면 새로 나온 ArrayList를  쓰기 바란다. 옛것은 좋은 것이나 프로그래밍의 세계에서는 새로운 것을 쓰는게 여러모로 유리하다. 수고는 덜 들면서 기능은 예전보다 좋아지고 강력해지는데 점점더 편리해지는 방식을 마다할 이유가 없기 때문이다.

다시 본론으로 돌아가서, 자바 5부터 등장한 대표적인 기능 가운데 제네릭(Generic)과 enhanced for 또는 for-each라 부르는 for loop 구문을 쓸수 있는데 이미 예전 강의에서 보여준적이 있지만 ArrayList에서 이를 접목시켜 구사함으로서 얼마나 코드가 간결해지고 명확해질수 있는지 예제를 통해 저절로 습득하게 될것이다.^^ 이번 예제에서는 ArrayList를 통해 어떻게 배열을 자유자재로 다룰수 있는지 주안점을 두어 구성하였다. 또한 고혈압이나 저혈압으로 고생하는 이들에게는 천금같이 귀중한 시간(?)이 될것이라 기대하면서 예제풀이를 시작하도록 하겠다.ㅎㅎ


고혈압에 좋은음식(al): 현미
고혈압에 좋은음식(al): 완두콩
고혈압에 좋은음식(al): 옥수수
고혈압에 좋은음식(al): 닭가슴살
고혈압에 좋은음식(al): 고등어
고혈압에 좋은음식(al): 고구마
고혈압에 좋은음식(al): 당근
고혈압에 좋은음식(al): 미역
고혈압에 좋은음식(al): 버섯
고혈압에 좋은음식(al): 양파
고혈압에 좋은음식(al): 김
1=== 강이의 JAVA강좌 ===1
고혈압에 좋은음식(copy): 현미
고혈압에 좋은음식(copy): 완두콩
고혈압에 좋은음식(copy): 옥수수
고혈압에 좋은음식(copy): 닭가슴살
고혈압에 좋은음식(copy): 고등어
고혈압에 좋은음식(copy): 고구마
고혈압에 좋은음식(copy): 당근
고혈압에 좋은음식(copy): 미역
고혈압에 좋은음식(copy): 버섯
고혈압에 좋은음식(copy): 양파
고혈압에 좋은음식(copy): 김
http://alecture.blogspot.com
완두콩,옥수수
고혈압에 좋은음식(array): 현미
고혈압에 좋은음식(array): 완두콩
고혈압에 좋은음식(array): 옥수수
고혈압에 좋은음식(array): 닭가슴살
고혈압에 좋은음식(array): 고등어
고혈압에 좋은음식(array): 고구마
고혈압에 좋은음식(array): 당근
고혈압에 좋은음식(array): 미역
고혈압에 좋은음식(array): 버섯
고혈압에 좋은음식(array): 양파
고혈압에 좋은음식(array): 김
2=== 강이의 자바강좌 ===2
저혈압에 좋은음식(al2): 고추
저혈압에 좋은음식(al2): 치즈
저혈압에 좋은음식(al2): 마늘
저혈압에 좋은음식(al2): 인삼
저혈압에 좋은음식(al2): 검은콩
3=== 강이의 자바강좌 ===3
고혈압에 좋은음식(sort): [고구마, 고등어, 김, 닭가슴살, 당근, 미역, 버섯, 양파, 옥수수, 완두콩, 현미]

예제에 대한 결과값은 위와같다. 이처럼 강이의 자바강좌는 여러분의 건강까지도 챙기는 강좌이다.ㅎㅎ 조금 길어보일지 모르겠는데 배열이라 그런 것이니 긴장풀기 바라고 언제나 그렇듯 예제의 내용을 알게되면 이런거였군! 별거없네~.라는 감탄사를 연발할수 있게 될것이니 편안하게 따라오기 바란다.ㅎㅎ 예제를 통해 여러분의 건강도 되찾을수 있을것이라 기대하면서 본격적으로 예제속으로 들어가보도록 하겠다.^^

일단 패키지에서 필요한것들을 import로 불러들였다. 어떤게 있는지 대충보기 바라고 본 소스에서 보면 ArrayList를 생성하는데 <String>이라는 문구를 썼다. 이렇게 뾰족한 괄호 < >가 나오면 여러분은 아~ 제네릭 등장이군!이라고 생각하면 되는데 그냥 안써도 되게 만들지 저렇게 데이터형을 쓰도록 했는지 질문하는 이들은 잘 듣기 바란다. "안써도 된다." 허걱~ ㅎㅎ 그런데 왜 쓰는 것일까?

반대로 그럼 쓰면 무슨 장점이 있을까? 제네릭의 강력한 무기 중 하나가 바로 저렇게 객체생성할때 타입을 지정해주면 지정된 타입이외에는 받아들이지 않는다. 이것은 여러분이 리스트를 구성할때 상당히 편리해지는데 간단한 예를 들어 사원들 이름을 입력받는다고 치자. 그럼 입력받을때 문자열이 아닌 정수나 부호들은 입력해도 그 즉시 거부할수 있는 프로그램 구성이 가능해진다. 그렇지 않을경우 저번처럼 입력받고 나서 후에 캐스팅을 통해 이게 문자인지 아닌지 검열해야하는데 반해 제네릭을 쓰면 컴파일시 사전에 이를 방지할수 있으니 얼마나 유리하겠는가? 이번 예제에서도 음식들이므로 아예 스트링 타입을 지정하고 쓴것이다. 이런 제네릭(Generics)의 기능을 우리는 type-safety에 유리하다고 표현한다.

객체생성시 제네릭 쓰는 방법은 예제처럼 클래스 이름옆에 원하는 데이터형을 양옆에 적어주면 되고 나머지는 평상시 코드처럼 쓰면 된다. 그리고 ArrayList al 에다가 순서데로 현미부터 김까지 쭈욱 배열리스트에 추가하였다. 그리고 확장된 for 룹을 돌려서 출력하는 부분이다. 물론 인덱스를 이용해 인자를 증감시키면서 기존의 for 룹을 쓸수 있으나 좋은길 놔두고 외딴길로 갈 필요는 없다하겠다. 그럼 for-each 확장된 for 룹을 쓰는 방법을 설명하겠다. 괄호안에 데이터형과 변수명을 지정해주고 콜론 : (세미콜론이 아니다.)을 찍은후에 관련된 배열리스트명를 적어주고 출력란에는 초기 변수명을 다시 넣어주면 차례대로 요소를 출력한다. 이렇게 컬렉션 요소값들을 확장된 for 룹을 통해서 편하게 찍을수 있다. 자주 쓰이는 표현이니 이번 시간에 확실하게 자기것으로 만들기 바란다. 허나 외울 필요는 없다. 외우기 싫어도 저절로 외우게 될것이다. 계속해서 다음 예문에 나올것이니까 말이다.^^

다음에 분리선을 찍고 이번에는 copyList라는 배열리스트를 만든다. 여기서는 ArrayList를 똑같이 카피해서 어떻게 쓸수 있는지 보여줄려고 만든 것이다. ArrayList의 addAll( ) 이라는 메소드를 이용하면 이렇게 똑같은 배열리스트를 만드는게 가능하다.

다음은 ArrayList를 일반 배열로 넘겨받는 방법이다. 예제에서는 2가지 방법을 보여주고 있는데 ArrayList의 size( ) 메소드를 이용하는 방법과 toArray( ) 메소드를 이용하는 방법이다. 참고로 toArray( ) 메소드는 AbstractCollection 클래스에서 상속받은 메소드이다. 보는바와 같이 for 룹에서 array 리스트 대신에 a 리스트를 써줘도 결과는 같다. 그리고 다음에 보면

System.out.println(a[1]+","+array[2]);

이런 것이 나오는데 배열이 제대로 만들어졌는지 그냥 테스트해 볼려고 넣은 것이다. 결과값 두개이상 출력할때는 + 로 연결해주고 가운데 " " 쌍따옴표들은 결과값들 중간에 콤마를 넣어주려고 쓴것이다. 다 아는 것인데 설명한다고 하는 이들이 있겠지만 저게 뭐지? 한 이들도 분명히 있었을 것이다.ㅎㅎ

다음은 일반배열의 요소값들을 바로 리스트로 만드는 과정이다. 총 3가지 방식으로 만들어 보았는데 지금 예제에서 실행하고 있는 방식이랑 주석처리해 놓은 두가지 방식중 하나를 여러분이 편한데로 써먹으면 될것이다. 필자가 추천하는 방식은
List<String> al2 = Arrays.asList("고추","치즈","마늘","인삼","검은콩");
요거다. 왜냐하면 가장 편하니까~ ㅎㅎ 그리고 for 룹을 이용해 출력하는 것인데 다 아는 것이니 다음으로 넘어가보자.

벌써 마지막이다. 자바 컬렉션 프레임워크에 있는 Collections 클래스의 sort ( ) 메소드를 이용하면 예제처럼 리스트의 요소들을 차례대로 이렇게 순서데로 출력하는게 가능하다. 덧붙이자면 Collections 클래스의 메소드들은 빠른 접근을 위해 모두 static으로 구성되어 있으므로 이렇게 편하게 사용가능하다. 솔직히 sort( ) 쓸때 한글은 안될줄 알았는데 작동이 되길래 조금 놀랐다. 자바의 놀라운 위력(?)에 감탄을 금하지 않을수 없다.ㅎㅎ ArrayList에서 while문을 통해 많이 쓰이는 Iterator와 ListIterator가 있는데 이건 여러분의 정신건강(?)을 위해 다음시간으로 넘기겠다.^^