Stack과 Queue는 배열과 유사하나, 데이터 처리에 있어 별도의 특성을 가지는 자료 구조(Data Structure)의 한 형태다(필자가 지금까지 자료구조라는 용어를 쓰지 않았는데, ArrayList, LinkedList 역시 자료 구조의 한 형태다). Stack과 Queue의 생김새는 배열과 동일한데, 데이터를 pop()으로 추출하고 삭제하는 방식에서 차이가 있다.
Stack은 후입선출(Last In First out: LIFO)의 특성을 가지며, Queue는 선입선출(First In First Out)의 특성을 가진다. 쉽게 풀자면 Stack은 배열 내에서 데이터 추출 시, 가장 마지막에 저장된 데이터가 먼저 추출되고, Queue는 가장 먼저 저장된 데이터가 먼저 출력된다.
Stack의 예시는 흔히 사용하는 워드나 한글같은 문서 작성 프로그램에서 많이 사용하는 Redo(되돌리기)와 Undo, 혹은 인터넷 브라우저의 뒤로가기 버튼을 들 수 있다. Queue는 순차적으로 발생한 작업 처리에 유용하기 때문에 프린터의 대기열 구성 시 많이 사용한다.
Stack과 Queue에 대한 간략한 설명이 끝났으니, 이 두 녀석들의 사용 방법을 알아보자. 내용이 많지는 않다.
1. Stack 클래스
다른 컬렉션 프레임워크와 마찬가지로 Stack 역시 java.util 패키지 내에 존재한다. 이 Stack은 특이하게도 컬렉션 프레임워크의 인터페이스인 List나 Map, Set을 구현하지 않고 Vector 클래스를 상속받는다. 즉, Vector와 동일한 기능에 5가지 연산이 추가되어 Vector를 Stack처럼 동작하도록 제작한 클래스라 보면 된다.
Stack은 인스턴스 생성 시 사용하는 생성자가 기본 생성자 뿐이다. Stack을 생성하면 크기가 10인 배열이 하나 생성되는데, Vector 매서드인 add(Object)를 사용하여 값을 추가할 수 있다. 물론, 저장되는 데이터 수가 배열 크기를 넘어가면 배열이 추가된다.
Stack 클래스는 Vector 클래스에서 제공하지 않는 pop()과 poll() 매서드를 정의하고 있는데, 이들이 ArrayList와 LinkedList에서 가장 첫 번 째 index에 저장된 배열값을 추출하고 배열에서 삭제하는데 반해, Stack에서는 배열의 가장 마지막 index에 저장된 배열값을 추출하고 배열에서 삭제한다.
Stack으로 간편하게 구현할 수 있는 것이 인터넷 브라우저의 뒤로가기 및 다음 페이지로 가기다.
위의 코드에서 Stack 객체 변수를 모두 static으로 지정한 이유는, 새로운 VisitRecorder 인스턴스가 만에 하나라도 실수로 생성되더라도 영향을 받지 않게 하기 위함이다(자세한 내용은 이 포스팅을 참고하자).
2. Queue 인터페이스
이번 소제목의 붉은 글씨를 주목하자. 지금까지 설명한 컬렉션 프레임워크와 달리, Queue는 클래스가 아닌 인터페이스로 정의되어 있다.
Java Documentation 문서를 보면, Queue 인터페이스를 구현한 클래스 목록이 여러가지가 나오는데, 필자가 바로 직전의 포스팅에서 설명했던 LinkedList의 경우, Queue 인터페이스를 구현한 클래스다. 따라서 Queue 사용을 하고자 한다면, 객체 타입은 Queue로 하되, 생성자는 Queue 인터페이스를 구현한 클래스로부터 호출하면 된다. 어제 사용했던 LinkedList를 Queue 형태로 만들어보자.
Queue는 LinkedList에서 사용가능한 매서드는 물론이고, Queue 인터페이스에 정의된 매서드도 사용할 수 있다. 특이한 것은 poll(), peek() 등 일부 매서드는 다른 클래스들과 달리 Queue 인터페이스에 정의된 매서드를 바로 사용한다는 것이다. 그렇다고 매서드 기능에 차이가 있거나 한 것은 아니기 때문에 사용 상에 큰 문제는 없다.
Queue 인터페이스에 정의된 매서드를 보면, 동일한 기능을 하는 매서드가 2개씩 존재하는 것을 알 수 있다. 값을 추가하는 것은 add()와 offer(), 가장 첫 번째 값을 삭제하는 것은 remove()와 poll(), 그리고 값을 조회하는 것은 element()와 peek()이 있다. 같은 기능을 함에도 매서드를 중복해서 만든 이유는 작업 시 발생하는 예외 때문이다.
add, remove, element는 예외 상황이 발생하면 예외를 던지고 프로그램을 중단하지만, offer(), poll(), peek()은 예외가 발생하더라도 null 값을 던짐으로써 프로그램 중단 없는 코드를 작성할 수 있다. 예를 들어, 빈 Queue 배열에 remove() 매서드를 호출하면 NoSuchElementException이 발생하나, poll()은 null을 던져 예외를 유발하지 않는다.
Queue로 문서를 출력하는 프로그램을 간략하게 구현하면 아래와 같다.
참고로 Queue는 단방향으로만 데이터가 이동하는 전통적인 Queue 외에도, 양방향으로 데이터를 추가, 추출할 수 있는 Dequeue도 존재한다. Queue가 offer()와 poll()만으로 데이터 추가, 추출을 진행한다면 Dequeue는 offerLast(), pollLast() 매서드로 다른 방향에서도 데이터 작업을 진행할 수 있도록 한다.
Stack과 Queue는 프로그래밍에서 자료구조의 아주 기초적인 내용이기 때문에, Java의 컬렉션 프레임워크를 사용하지 않고 순수 배열만으로 구현해보는 것도 실력 향상에 많은 도움이 된다(라고 필자는 말하지만 필자도 시간 상 Java로는 아직 진행해보지 못했다...).
다음 포스팅에서는 Iterator, ListIterator, Enumeration에 대해 알아보려 한다.
Fin.
'Java > Java Basic' 카테고리의 다른 글
[Java Basic] 35 - 컬렉션 프레임워크5 (Arrays, Comparable, Comparator) (0) | 2022.09.05 |
---|---|
[Java Basic] 34 - 컬렉션 프레임워크4 (Iterator, ListIterator, Enumeration) (0) | 2022.09.02 |
[Java Basic] 32 - 컬렉션 프레임워크2 (ArrayList, LinkedList) (0) | 2022.09.01 |
[Java Basic] 31 - 컬렉션 프레임워크 개요 및 Vector 클래스 (0) | 2022.08.29 |
[Java Basic] 30. 형식화 클래스 (0) | 2022.08.26 |
댓글