- 후입 선출(LIFO; Last-In, First-Out) 구조의 자료구조
: 가장 나중에 저장된 데이터부터 가장 먼저 꺼내는 방식의 자료구조- 일반적으로 배열 기반 컬렉션 클래스로 구현
: 데이터 추가는 항상 순차적으로 수행되고, 삭제 또한 맨 마지막부터 역순으로 진행하므로
추가/삭제 시 추가 작업이 불필요하기 때문- 다음과 같은 경우에 활용
: 수식 계산, 수식 괄호 검사, 워드프로세서의 undo/redo, 웹브라우저의 뒤로/앞으로 기능 등
Stack 인터페이스
package datastructure.stack;
public interface Stack<E> {
boolean isEmpty();
void push(E e); // 스택에 새로운 데이터를 추가, 늦게 저장될수록 맨 위에 저장
E pop(); // 스택의 맨 위에 저장된 데이터를 삭제
E peek(); // 가장 최근에 저장된 데이터를 반환
void clear(); // 스택에 저장된 모든 데이터를 제거
int getSize(); // 스택에 저장된 데이터의 개수 반환
void dump(); // 스택에 저장된 모든 데이터를 출력
}
ArrayBaseStack 클래스
import java.util.Arrays;
public class ArrayBaseStack<E> implements Stack<E> {
private static final int DEFAULT_CAPACITY = 10; // 용량의 초기값
private final int max; // 스택의 최대 용량
private final Object[] stack; // 스택의 본체가 되는 배열
private int top; // 스택의 꼭대기에 해당하는 인덱스 번호
// 지정한 용량을 갖는 스택을 생성
public ArrayBaseStack() {
this(DEFAULT_CAPACITY); // 별도의 용량을 지정하지 않으면 초기값으로 설정
}
public ArrayBaseStack(int max) {
this.max = max; // 스택에 허용된 용량 초기화
this.stack = new Object[max]; // 지정한 용량을 갖고 스택의 본체가 되는 배열 생성
this.top = 0; // 스택의 꼭대기가 되는 인덱스 번호 초기화
}
// 빈 스택인지 확인
@Override
public boolean isEmpty() {
return top <= 0;
}
// 가득 찬 스택인지 확인(배열 기반 스택에서만 이용 가능한 기능)
public boolean isFull() {
return top >= max;
}
@Override
public void push(E e) {
// 1-1. 스택이 가득 찬 경우 그대로 종료
if (isFull()) {
System.out.println("Stack is full");
}
// 1-2. 스택의 꼭대기에 새로운 데이터를 추가
// 2. 꼭대기가 되는 인덱스 번호는 1 증가
stack[top++] = e;
}
@Override
public E pop() {
// 1-1. 스택이 비어 있는 경우 그대로 종료
if (isEmpty()) {
return null;
}
// 구현한 스택을 사용할 실행 코드에서는 실행 도중 스택 내 데이터의 형변환을 수행하지 않음
// 1-2. 스택에 꼭대기가 되는 인덱스 번호를 1 감소시켜 스택의 범위 축소
@SuppressWarnings("unchecked")
E data = (E) stack[--top];
// 2. 삭제된 데이터를 반환
return data;
}
@Override
public E peek() {
// 1-1. 스택이 비어 있는 경우 그대로 종료
if (isEmpty()) {
return null;
}
// 1-2. 스택에 데이터가 존재하면 스택의 꼭대기에 저장된 값을 반환
@SuppressWarnings("unchecked")
E data = (E) stack[top - 1];
return data;
}
// 스택을 비움
@Override
public void clear() {
Arrays.fill(stack, null);
top = 0;
}
// 스택의 용량을 반환(배열 기반 스택에서만 이용 가능한 기능)
public int getCapacity() {
return max;
}
// 스택에 축적된 데이터의 개수를 반환
@Override
public int getSize() {
return top;
}
// 스택에 저장된 모든 데이터를 바닥~꼭대기 순으로 출력
@Override
public void dump() {
if (isEmpty()) {
System.out.println("Stack is empty!!");
return;
}
for (int i = 0; i < top; i++) {
System.out.print(stack[i] + " ");
}
System.out.println();
}
}
실행용 샘플 코드 및 실제 실행 결과
public class ArrayBaseStackMain {
public static void main(String[] args) {
ArrayBaseStack<Integer> stack = new ArrayBaseStack<>();
// 스택에 1~5까지의 정수를 순서대로 저장
for (int i = 1; i <= 10; i++) {
stack.push(i);
}
// 현재 스택에 저장된 데이터 개수, 저장된 데이터(꼭대기 → 바닥 순) 출력
System.out.printf("Stack capacity: %d, Stack size: %d\n", stack.getCapacity(), stack.getSize());
stack.dump();
if (stack.isFull()) {
System.out.println("Stack is full!!");
}
System.out.println();
// 스택에 저장된 1개씩 꺼냈을 때, 꺼낸 데이터와 그 때의 스택의 크기 출력
while (!stack.isEmpty()) {
System.out.printf("Popped: %d, Stack size: %d\n", stack.peek(), stack.getSize());
stack.pop();
}
System.out.println();
// 현재 스택이 빈 스택인지 출력
System.out.printf("Stack capacity: %d, Stack size: %d\n", stack.getCapacity(), stack.getSize());
stack.dump();
}
}
ListBaseStack 클래스
package datastructure.stack;
import datastructure.util.Node1;
public class ListBaseStack<E> implements Stack<E> {
private Node1<E> top; // 스택의 꼭대기 노드를 가리키는 변수
private int size; // 스택에 저장된 노드의 개수
// 스택 초기화
public ListBaseStack() {
top = null;
size = 0;
}
// 빈 스택인지 확인
@Override
public boolean isEmpty() {
return top == null;
}
@Override
public void push(E e) {
// 1. 주어진 데이터를 저장할 새로운 노드 생성
Node1<E> newNode = new Node1<>(e, top);
// 2. 새로운 데이터는 기존의 꼭대기 위에 추가
newNode.setNext(top);
// 3. 꼭대기 노드를 새로 추가된 노드로 갱신
top = newNode;
// 4. 스택에 저장된 노드의 개수 1 증가
size++;
}
@Override
public E pop() {
// 1-1. 빈 스택일 경우 그대로 종료
if (isEmpty()) {
return null;
}
// 1-2. 삭제할 데이터는 별도로 저장
E removedData = top.getData();
// 2. 스택의 꼭대기 노드를 바로 밑의 노드로 갱신하여 기존의 꼭대기 노드 삭제
top = top.getNext();
size--;
return removedData;
}
// 현재 스택의 꼭대기에 저장된 데이터를 반환
@Override
public E peek() {
return top.getData();
}
// 스택에 저장된 모든 데이터 삭제
@Override
public void clear() {
while (!isEmpty()) {
pop();
}
top = null;
}
// 스택에 저장된 노드의 개수를 반환
@Override
public int getSize() {
return size;
}
// 스택에 저장된 모든 데이터를 꼭대기에서 바닥 순으로 출력
@Override
public void dump() {
if (isEmpty()) {
System.out.println("Stack is empty!!");
return;
}
Node1<E> current = top;
while (current != null) {
System.out.println(current.getData());
current = current.getNext();
}
}
}
실행용 샘플 코드 및 실제 실행 결과
package datastructure.stack;
public class ListBaseStackMain {
public static void main(String[] args) {
Stack<Integer> stack = new ListBaseStack<>();
// 스택에 1~5까지의 정수를 순서대로 저장
for (int i = 1; i <= 5; i++) {
stack.push(i);
}
// 현재 스택에 저장된 데이터 개수, 저장된 데이터(꼭대기 → 바닥 순) 출력
System.out.printf("Stack size: %d\n", stack.getSize());
stack.dump();
System.out.println();
// 스택에 저장된 1개씩 꺼냈을 때, 꺼낸 데이터와 그 때의 스택의 크기 출력
while (!stack.isEmpty()) {
System.out.printf("popped: %d, stack size: %d\n", stack.peek(), stack.getSize());
stack.pop();
}
System.out.println();
// 현재 스택이 빈 스택인지 출력
System.out.println("Stack size: " + stack.getSize());
stack.dump();
}
}
- Java 언어에서 스택 자료구조는
java.util.Stack
,java.util.ArrayDeque
,java.util.LinkedList
등의 클래스에서 구현됨- Java에서는 스택 자료구조 사용 시
java.util.Stack
클래스보다는java.util.ArrayDeque
등의 덱 자료구조의 기능을 사용할 것을 권장
:java.util.Stack
의 조상 클래스인java.util.Vector
자체의 성능 문제도 존재하지만, 덱 자료구조 자체는 스택/큐의 기능을 모두 사용 가능하여 활용성이 높기 때문
(java.util.ArrayDeque
등의 덱 자료구조 관련 기능은 따로 정리)
스택 자료구조 사용 예시
/* 1. java.util.Stack 클래스 사용*/
Stack<Integer> stackExample1 = new Stack<>();
/* 2. 덱 자료구조의 기능을 스택처럼 활용 */
Deque<Integer> stackExample2 = new ArrayDeque<>();
Deque<Integer> stackExample3 = new LinkedList<>();