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가 있는데 이건 여러분의 정신건강(?)을 위해 다음시간으로 넘기겠다.^^

2012년 10월 18일 목요일

자바의 벡터(Vector)




자바의 벡터 Vector 클래스에 대해서 공부해 보기로 할텐데 위의 그림들은 오늘 배울 자바의 벡터에 관련된 계보도이다. 자바의 벡터는 JDK 1.2 버젼부터 자바 컬렉션 프레임워크의 List를 구현한 멤버로서 재해석되었다. 컬렉션에 대해서는 차후에 설명할테니 궁금하더라도 지금은 넘어가기 바란다.ㅎㅎ 보는바와 같이 자바의 벡터 클래스는 java.util 팩키지에 들어있으므로 클래스를 만들기에 앞서 import java.util.Vector; 라고 먼저 관련 패키지를 불러들인후에 사용할 것임을 예측할수 있을 것이다. 예측하면서 본 이가 있으려나? ㅎㅎ 물론 클래스안에서 java.util.Vector를 직접적으로 불러서 쓸수도 있으나 보기 지저분하니 필자같이 청결한 이는 보통 쓰지 않는다.ㅎㅎ 더불어 Vector 클래스안에 어떤 메소드들이 있는지 눈요기좀 하라고 한것이고 다른 클래스들도 앞으로 하나둘씩 다루게 될것이라 미리 구경하라고 올려놓았는데 이런 필자의 뜨끈뜨끈한 배려심에 뭉클한 뭔가(?)가 전해졌길 바라면서 본론으로 들어가겠다.^^

자바의 배열을 공부하고 나서 슬슬 눈에 띄기 시작하는 것이 바로 Vector라는 녀석일 것이다. 비단 이 녀석뿐 아니라 배열의 형태가 워낙 쓰이는 곳이 많으므로 배열에서 확장된 개념의 클래스들이 상당히 많은데 오늘은 그중에서도 배열의 길이와 데이터형을 자유자재로 주무를수 있는 벡터에 대해서 공부해 보겠다.^^ 시작하기전에 워밍업을 하자면 강이의 자바강좌에서 벡터를 처음보는 접하는 이도 있겠지만 그렇지 않고 벡터에 대해서 잘 모르는 태반은 아마도 너무 복잡해 보여서 아니면 이것저것봐도 이해가 잘 안되어서 망설이다 여기까지온 이들이 많을 것이다. 그렇다면 모두 환영한다. 벡터에 지친 자들아, 모두 내게로 오라~ㅎㅎ

오늘 잠시만 이 강의에 집중하면 여러분은 자바의 벡터에 대해서 상당한 자신감으로 무장하게 될것이라 단언한다. 더군다나 이번 강의는 다른걸 공부하라고 강요하지도 않겠다. 그냥 딱 한가지만 하면된다. 예제 딱 하나~ 이것만 공부하면 벡터에 대해서 어느정도 감을 잡게 될것이라 확신한다. 모든 것이 단 하나의 예제로 시작해서 예제로 끝나도록 필자가 이 예제 한편에 벡터의 초초초오 엑기쓰만 쎄리 퍼부었다는 점을 강력히 밝힌다. 따라서 다른건 몰라도 오늘 보여줄 이 예제에 대해서만큼은 필사적으로 타이핑(?)하고 가능하면 프린트까지해서 같이 따라오기 바란다.ㅎㅎ 자 시간 10분 주겠다. 딴짓하지 말고 아래 예제를 클릭해서 빨리 타이핑하고 실행해보기 바란다.^^


3
10
3
강이의 JAVA강좌
5
4
5
3
start
10
end
강이의 자바강좌
end
10
start
middle
true
false
5
true
0

==========
예제를 실행하면 위와같이 결과값이 찍힐 것이다. 이제 본격적으로 예제분석에 들어가보기로 하겠다. import 부분은 관련 클래스 쓸려고 가져온 것이니 넘어가고 이제 메인메소드 안으로 들어가 보겠다. 일단 벡터를 사용하기 위해 벡터 클래스를 이용해서 객체생성을 하고 인스턴스 인자로 v를 만들었는데 괄호안에 (3,2)가 있다. 이게 무슨 뜻인가 하면 벡터의 길이를 3으로 하고 모자르면 길이를 2씩 증가시키라는 명령이다. 만약 괄호안에 아무것도 쓰지않고 벡터의 객체생성을 하였다면 기본적으로 길이는 10으로 할당된다. 그리고 모자르면 원래 기본길이의 두배씩 증가시킨다. 20  40  80 이런식으로 말이다. 따라서 어느정도 예측이 가능하다면 효율성을 고려해 제어시켜주는 것이 바람직하다. 옆에 주석을 달아놓은 것은 여러분이 이해하기 쉽도록 벡터가 어떻게 변해가는지 써놓은 것이니 조금이나마 벡터의 개념을 여러분이 이해하는데 도움이 되기를 간절히 희망한다.^^

다음 코드에서는 v.capacity( ) 즉 벡터의 용량메소드를 이용해서 프린트하라는 것인데 이 메소드가 하는 일이 벡터의 용량 즉 얼마만큼의 요소를 집어넣을수 있는지 길이를 알려주는 메소드다. 벡터의 메소드들이 무엇이 있고 무슨 일을 하는지는 자바 API를 찾아보면 다 나오니 귀찮더라도 한번씩 보면서 쫒아오기 바란다.ㅎㅎ 하여간 그래서 결과에 3 이라는 숫자가 처음으로 찍히는거다.

다음은 add 메소드를 쓰고 있다. 그럼 여러분은 바보가 아닌이상 강력한 자바삘(?)이 오고 있을 것이다. 뭔가를 집어넣는구나~ 그렇다. 예제에서는 start라는 문자를 집어넣고 있다. 그래서 주석에서 보는 것처럼 처음에 start라는 문자가 들어간거다. 자 여기서 여러분들이 놀라야하는 부분이 있다. 응?? 문자열을 넣는데 뭐 스트링을 이용해서 정의하거나 데이터형을 알려주는 과정도 없이 그냥 벡터에다가 넣어버렸다. 아 놀랍지 않은가? 그냥 놀랍다고 하자.ㅎㅎ 놀라움의 연속으로 다음 코드에 정수 10도 저렇게 그냥 넣어버릴수 있다. 캬하~ add( ) 메소드나 addElement( ) 메소드나 하는 일이 같은데 그 다음줄을 보면 add( ) 메소드가 더 뛰어나구나라는 것을 느낄수 있다. add( ) 메소드는 예제처럼 벡터의 인덱스를 지정하고 그곳에 원하는 요소를 집어넣을수가 있는데 예제에서는 인덱스 1에다가 ok라는 문자를 집어넣으라고 했으니 옆의 주석에서처럼 ok가 중간에 들어가게된다. 인덱스는 항상 0에서 시작하니까 말이다. 이거 너무 자세히 설명하니 진도 안나간다고 여기저기서 항의하는게 들려오는 것 같구만.ㅎㅎ

다음은 벡터의 get( ) 메소드를 이용해서 프린트하라는 것인데 이 메소드가 하는일은 말그대로 얻어서 가져오는 것이다. 괄호안의 2는 인덱스 2의 위치에 해당하는 요소를 가져오라는 것이다. 따라서 10이라는 숫자가 출력된다. 그 다음에 다시 벡터길이에 대한 용량을 찍으라고 하니 변한게 없으니까 아까처럼 3을 찍을 것이다. 그리고나서 강이의 JAVA강좌를 찍는다.ㅎㅎ 광고가 아니라 공부할때 따라가면서 결과값 보기 좋으라고 써넣은 것이니 필자를 탓하지 말아라.^^

자 이제 벡터의 새로운 마법(?)이 펼쳐진다. 20을 넣었는데 어라.. 다음 코드에서 벡터용량을 찍어보니 신기하게도 길이가 5로 증가하였다. 왜 벡터용량의 길이가 3에서 5가 되었을까? 기억이 안난다면 메인메소드의 첫째줄로 가보면 답이 있다. 객체생성을 할때 처음 길이용량을 3이라 놓고 모자르면 2씩 증가시키라고 하지 않았는가? 바로 그렇다. 벡터가 이미 꽉 차 있었으므로 20을 넣을때 우리가 설정해놓은 자동수치인 2를 증가시켜 총 길이용량 5가 되는 벡터를 자동으로 만든 것이다. 그리고 다음줄에 보면 size( ) 메소드를 이용해 프린트하라고 하였는데 4가 찍힌다. 여기서 size란 무엇을 뜻하는 것일까? 여기서 사이즈는 벡터에 있는 요소들의 갯수를 알려달라는 의미다. 지금 길이용량이 5인 벡터이나 안의 요소는 주석에서 보는 것처럼 하나가 모자란 4개의 요소들만 있다. 우리가 지금까지 4개의 요소들만 넣어왔으니까 말이다.^^

다음 코드는 벡터에 end를 넣은 것이고 그 다음줄을 보면 처음봐도 감이 올것이다. remove( ) 메소드를 썼으니 뭔가 없애라는 것일텐데 괄호안에 ok를 써놨다. 여기에서 벡터의 또다른 놀라운 힘을 느낄수 있다. 이렇게 인덱스를 이용하지 않고 요소를 직접적으로 지정해도 관련 요소를 벡터에서 없애버릴수 있으니 말이다. 이렇게 요소가 제거되면 주석에서 보는 것처럼 벡터 요소들이 알아서 저렇게 정렬된다. 다음줄은 인덱스로 관련 요소를 제거하는 방법이다. 2를 넣었으니 정렬된 벡터에서 다시 인덱스 2에 해당하는 위치의 요소인 20을 없애게 되고 또다시 벡터 요소들이 알아서 정렬된다. 자유자재로 정렬하는 벡터의 기능이 기막히지 않는가? 놀라는 척이라도 혀~ㅎㅎ

다음줄은 벡터 길이용량을 찍으라는건데 보다시피 벡터의 요소가 제거된다고 해서 벡터의 길이가 줄어드는게 아니라는걸 알수 있다. 그러니 당연히 5가 찍힌다. 그런데 이제 넣을것도 없는데 용량이 아까우니 벡터 길이용량을 줄여버리자는 생각이 불현듯 든다. 그때 유용하게 써먹을수 있는 메소드가 바로 trimToSize( ) 메소드다. 이 메소드를 사용하면 주석에서 보는 것처럼 벡터의 요소들에 맞게 알아서 길이를 맞춰준다. 지금은 요소가 3개 있으니까 알아서 맞춰준다면 길이용량이 3이라고 찍혀야 되는데 아니나 다를까 아래에 출력되는 것을 보니 3이 찍히므로 제대로 작동되고 있다는 것을 알수 있다. 잠깐~ 필자가 지금 너무 힘들다.. 물좀 먹고 올테니 1분간만 쉬자.ㅎㅎ

자 다시 예제로 와서 보면 Enumeration이 나온다. 아까 패키지 불러들일때 썼으니 이거 써먹을거라고 짐작하고 있었을텐데 사실 이 부분이 오늘 이해하기힘든 가장 심오(?)한 부분이겠지만 필자가 최대한 간략하게 설명해 보겠다. 자바의 JDK 1.2 다시 말해, 자바 2에서 많은 기능들이 추가되었는데 그중에 하나가 바로 Vector와 같은 컬렉션 클래스들(위의 계보도 그림 보면서 다른게 뭐가 있는지 찾아보기 바란다.^^)을 관리하는 컬렉션 프레임워크(Collections Framework)라는 것이다. 물론 벡터는 예전부터 있어왔었다. 이 컬렉션 프레임워크에서는 Collection 인터페이스를 상속받은 모든 컬렉션 클래스에서 Enumeration을 사용가능하게 해놓았다는 것이 요지라 할수 있겠다. Enumeration은 자바의 초창기 버젼 1.0부터 있어왔던 것인데 이걸 조금더 발전시켜서 객체요소를 제거할수 있는 기능까지 첨가한 Iterator 같은 인터페이스도 이때 나오게 된것이다. 지금 예제에서 Enumeration을 썼는데 새로 나온 Iterator를 쓰는것도 가능하니 그건 여러분이 한번 도전해보기 바란다. 예제 코드를 조금만 바꾸면 되니 그리 어렵지 않을 것이다. 번외편이 너무 길었던것 같으니 다시 예제로 돌아가자.ㅎㅎ

이 Enumeration이라는 인터페이스는 Collection 인터페이스로 구현한 클래스안에 저장되어 있는 객체들을 손쉽게 꺼낼수 있도록 해준다. 우리가 지금 공부하고 있는 벡터 클래스가 바로 컬렉션 인터페이스 자체를 직접적으로 구현한 클래스이므로 앞서 말한바와 같이 이렇게 손쉽게 사용이 가능하다.^^ 옆을 보면 벡터의 elements( ) 메소드를 인자 e에 집어넣으라고 하는데 이 메소드가 하는 일이 벡터 안에있는 요소들을 모두 가져오라는 것이다. 그리고 안에 무엇이 있는지 프린트하려고 while문을 돌리고 있는데 Enumeration에 있는 hasMoreElements( )와 nextElement( ) 메소드를 이용해서 안에 값이 있는지 확인하고 있으면 true, 없으면 false값을 돌려주며 있을 경우 요소들을 차례차례 찍으라는 구문이다. 벡터의 get(i) 메소드 같은걸 이용해 증감인자를 써서 루프를 돌릴수도 있겠지만 보는바와 같이 예제처럼 Enumeration을 활용하면 아주 쉽게 처리가 가능하니 이렇게도 쓸수 있다는 것을 알아두기 바란다.^^ 그래서 결과값을 보면 벡터 요소들인 start  10  end가 차례차례 찍힌다.

다음줄로 와서보면 잠시 광고 아니 공부하기 좋으라고 신경쓴 문구(?)가 나오고 방금 얘기한 벡터 get( ) 메소드를 이용해서 괄호안의 각 인덱스에 해당하는 요소들을 출력한다. 그래서 순서데로 end  10  start 가 찍히는 것이다.

다음줄에 보면 insertElementAt( )이라는 메소드를 썼는데 이 메소드는 말그대로 어떤 위치에 요소를 집어넣으라는 것이다. 괄호를 보면 middle이라는 요소와 숫자 2가 적혀있는데 인덱스 2에 해당하는 위치에다 본 요소를 집어넣으라는 뜻임을 알수 있을 것이다. 그리고 주석에서 보는 것처럼 벡터의 길이용량이 이번에 또 5로 늘어난다. 저번처럼 middle을 넣기전에 이미 벡터 용량이 꽉 차 있었으니까 말이다. 아까 언급했으니 두번 설명 안하겠다.^^

다음 코드를 보면 elementAt( ) 메소드를 이용하였는데 이건 어떤 위치의 요소를 가져오라는 것인데 예제에서는 인덱스 2에 해당하는 요소를 가져오라고 하였으니 middle을 가져오게 되는데 이걸 캐스팅해서 스트링 변수 s에다가 넣어주었다. 캐스팅은 데이터형을 변환할때 사용하는 방식인데 저렇게 앞에다가 바꾸고자 하는 데이터 타입명을 써주고 괄호로 감싸주면 된다. 여기서 의아해하는 이들이 있을지도 모르겠다. middle이 스트링이지 않느냐? 데이터형이 같은데 왜 캐스팅을 하냐고 말이다. 좋은 질문이다.ㅎㅎ 벡터는 요소들을 몽땅 Object 형태로 보관한다. 그래서 데이터형에 관계없이 저렇게 자유롭게 쓸수 있는 것이다. 허나 이걸 끄집어내서 쓰려면 요소값에 맞는 데이터형으로 다시 변환시켜줘야하므로 캐스팅을 이용한거다.^^ 그냥 벡터에 있는 메소드 이용해서 출력해도 되지만 이렇게 관련 변수로 넘겨받아 출력도 가능하다는 것을 보여주기 위해서 필자가 이처럼 어려운 길을 걷고 있는 것이다.ㅎㅎ 그래서 다음줄에 s를 출력하면 middle이 찍힌다.

이번에는 boolean이 나왔다. 참인지 거짓인지를 알아보자는건데 변수를 bool로 만들고 값을 넘겨받으려고 보니 벡터의 contains( ) 메소드라는 것이 보인다. 이 메소드가 하는 일은 요소값을 넣어주면 벡터에서 그 요소가 있는지를 찾고 있으면 true, 없으면 false값을 돌려준다. 예제에서는 end가 있는지를 찾으라고 했는데 있으므로 처음엔 true가 출력되고 다음에 20이 있는지를 찾으라고 해서 찾아보니 이번엔 없어서 false를 출력하는 부분이다. 이제 다 끝나간다. 힘내자.ㅎㅎ

다음 코드를 보니 removeAllElements( ) 메소드가 나온다. 이거야 하는일을 모르는 이가 없을거라 본다. 말그대로 모든 요소들을 제거하라는 것이니 주석에서처럼 텅텅 비게 된다. 그래서 다음줄에 벡터 길이용량을 체크하라고 했더니 5가 찍힌다. 요소들이 제거되어도 벡터 용량과는 상관없음을 알수 있고 이것 대신에 벡터의 clear( ) 메소드를 쓸수도 있다. 다음 코드도 뜻이 명확하다. 안에 비었냐? 당연히 안이 비었으니까 true값을 돌려준다. 다르게 설명하자면 isEmpty( ) 메소드는 사이즈가 0일 경우에만 true를 리턴하고 그 이외의 경우에는 false를 리턴한다. 여기서 사이즈란 아까 배웠다시피 객체요소들의 갯수를 의미하는 것이고 다음줄이 바로 그것을 물어보는 것인데 아까 요소들을 제거해서 벡터에 어떤 요소도 없으므로 size( ) 메소드 값은 0이 찍힌다. 벡터에서 size( ) 메소드는 벡터의 길이 사이즈를 물어보는 것이 아님을 알아두기 바란다.^^

이로써 오늘 벡터 강좌를 마치도록 하겠다. 해보니까 다 이해가 가는 자기자신에게 놀라고 있진 않은가? 만약 그렇다면 자기자신을 향해 박수 10번을 치며 자축하기 바란다. 이번달이 10월이니까 말이다.ㅎㅎ 무슨 헛소리를 하는지 모르겠다. 알아서 마무리 잘하고 필자는 급피곤한 관계로 박수칠때 물러가도록 하겠다.^^