Java에서 스택을 활용한 효율적인 데이터 처리와 계산 기법

스택의 개념과 Java에서의 구현 방식

스택(Stack)은 후입선출(LIFO: Last In, First Out) 원칙에 따라 동작하는 자료구조로, 데이터의 추가와 제거가 한쪽 끝에서만 이루어진다. 이 구조는 함수 호출 관리, 수식 계산, 문법 분석 등 다양한 소프트웨어 설계 영역에서 핵심적인 역할을 한다.

Java에서는 java.util.Stack 클래스를 통해 기본적인 스택 기능을 제공하지만, 내부적으로 Vector를 상속받아 스레드 안전성을 보장하기 때문에 불필요한 성능 오버헤드가 발생할 수 있다. 따라서 동시성 제어가 필요하지 않은 일반적인 경우, Deque 인터페이스를 구현한 ArrayDeque를 사용하는 것이 더 효율적이다.

ArrayDeque를 이용한 스택 구현

다음 예제는 ArrayDeque를 스택처럼 사용하는 방법을 보여준다.

import java.util.ArrayDeque;
import java.util.Deque;

public class EfficientStack {
    public static void main(String[] args) {
        Deque<String> stack = new ArrayDeque<>();

        // 요소 삽입
        stack.push("첫 번째");
        stack.push("두 번째");
        stack.push("세 번째");

        System.out.println("최상단 요소: " + stack.peek()); // 출력: 세 번째

        System.out.println("팝된 요소: " + stack.pop()); // 출력: 세 번째

        System.out.println("스택이 비었는가? " + stack.isEmpty()); // 출력: false
    }
}

수식 평가에서의 스택 활용

연산자 우선순위를 고려한 중위 표기식의 계산은 스택을 사용하면 간결하게 해결할 수 있다. 피연산자는 하나의 스택에, 연산자는 다른 스택에 저장하면서 우선순위에 따라 연산을 수행한다.

import java.util.Stack;

public class InfixEvaluator {
    public static int evaluateExpression(String expr) {
        Stack<Integer> operands = new Stack<>();
        Stack<Character> operators = new Stack<>();

        for (int i = 0; i < expr.length(); i++) {
            char ch = expr.charAt(i);

            if (Character.isDigit(ch)) {
                operands.push(ch - '0');
            } else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
                while (!operators.isEmpty() && getPriority(operators.peek()) >= getPriority(ch)) {
                    int result = compute(operands.pop(), operands.pop(), operators.pop());
                    operands.push(result);
                }
                operators.push(ch);
            }
        }

        while (!operators.isEmpty()) {
            operands.push(compute(operands.pop(), operands.pop(), operators.pop()));
        }

        return operands.pop();
    }

    private static int getPriority(char op) {
        return switch (op) {
            case '+', '-' -> 1;
            case '*', '/' -> 2;
            default -> 0;
        };
    }

    private static int compute(int b, int a, char op) {
        return switch (op) {
            case '+' -> a + b;
            case '-' -> a - b;
            case '*' -> a * b;
            case '/' -> {
                if (b == 0) throw new ArithmeticException("0으로 나눌 수 없습니다.");
                yield a / b;
            }
            default -> 0;
        };
    }

    public static void main(String[] args) {
        System.out.println(evaluateExpression("3+5*2")); // 출력: 13
        System.out.println(evaluateExpression("10*2+7")); // 출력: 27
    }
}

괄호 유효성 검사

문자열 내 괄호 쌍의 균형 여부를 판단하는 문제는 스택의 전형적인 응용 사례다. 여는 괄호는 스택에 넣고, 닫는 괄호가 나타나면 스택 최상단과 비교하여 일치하는지 확인한다.

import java.util.Stack;

public class BracketValidator {
    public static boolean isValidPair(String input) {
        Stack<Character> bracketStack = new Stack<>();

        for (char c : input.toCharArray()) {
            if (c == '(' || c == '[' || c == '{') {
                bracketStack.push(c);
            } else if (c == ')' && (bracketStack.isEmpty() || bracketStack.pop() != '(')) {
                return false;
            } else if (c == ']' && (bracketStack.isEmpty() || bracketStack.pop() != '[')) {
                return false;
            } else if (c == '}' && (bracketStack.isEmpty() || bracketStack.pop() != '{')) {
                return false;
            }
        }

        return bracketStack.isEmpty();
    }

    public static void main(String[] args) {
        System.out.println(isValidPair("{[()]}")); // true
        System.out.println(isValidPair("((("));   // false
        System.out.println(isValidPair("{[]}"));  // true
    }
}

JVM 내부의 스택 메커니즘

자바 가상 머신(JVM)은 각 스레드마다 호출 스택(call stack)을 유지하며, 메서드 실행 시마다 스택 프레임(stack frame)을 생성한다. 이 프레임에는 로컬 변수, 연산 스택, 메서드 반환 주소 등이 포함된다. 재귀 호출이 깊어질 경우 스택 공간을 초과하여 StackOverflowError가 발생할 수 있으므로, 반복문 기반의 구현이나 최적화된 알고리즘 설계가 중요하다.

태그: java Stack deque arraydeque 데이터구조

6월 12일 22:58에 게시됨