Infix to Postfix in Java

A complete infix to postfix converter in Java — the conversion, a postfix evaluator, and why ArrayDeque beats java.util.Stack. Both listings were compiled and run before publishing.

The complete Java program

Save this as InfixToPostfix.java. It uses only the standard library, compiles clean under -Xlint:all, and handles multi-character operands, decimals, all six operators and nested brackets.

InfixToPostfix.java
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

/** Converts an infix expression to postfix (Reverse Polish Notation) using a stack. */
public class InfixToPostfix {

    private static final Map<Character, Integer> PRECEDENCE = Map.of(
            '^', 3,
            '*', 2, '/', 2, '%', 2,
            '+', 1, '-', 1);

    private static final Set<Character> RIGHT_ASSOCIATIVE = Set.of('^');

    private static boolean isOperator(char c) {
        return PRECEDENCE.containsKey(c);
    }

    private static int precedence(char op) {
        return PRECEDENCE.getOrDefault(op, 0);
    }

    /** Returns the postfix form, with tokens separated by spaces. */
    public static String convert(String expression) {
        StringBuilder output = new StringBuilder();
        Deque<Character> stack = new ArrayDeque<>();
        int i = 0;

        while (i < expression.length()) {
            char c = expression.charAt(i);

            if (Character.isWhitespace(c)) {
                i++;
                continue;
            }

            // Copy a whole operand so "rate" and "12" stay single tokens.
            if (Character.isLetterOrDigit(c) || c == '_' || c == '.') {
                int start = i;
                while (i < expression.length()
                        && (Character.isLetterOrDigit(expression.charAt(i))
                            || expression.charAt(i) == '_'
                            || expression.charAt(i) == '.')) {
                    i++;
                }
                output.append(expression, start, i).append(' ');
                continue;
            }

            if (c == '(') {
                stack.push(c);
            } else if (c == ')') {
                while (!stack.isEmpty() && stack.peek() != '(') {
                    output.append(stack.pop()).append(' ');
                }
                if (stack.isEmpty()) {
                    throw new IllegalArgumentException("unbalanced brackets: an extra ')'");
                }
                stack.pop();                       // discard the matching '('
            } else if (isOperator(c)) {
                while (!stack.isEmpty()
                        && stack.peek() != '('
                        && (precedence(stack.peek()) > precedence(c)
                            || (precedence(stack.peek()) == precedence(c)
                                && !RIGHT_ASSOCIATIVE.contains(c)))) {
                    output.append(stack.pop()).append(' ');
                }
                stack.push(c);
            } else {
                throw new IllegalArgumentException("invalid character: '" + c + "'");
            }
            i++;
        }

        while (!stack.isEmpty()) {
            char top = stack.pop();
            if (top == '(') {
                throw new IllegalArgumentException("unbalanced brackets: a missing ')'");
            }
            output.append(top).append(' ');
        }

        return output.toString().trim();
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter an infix expression: ");
        String infix = scanner.nextLine();
        try {
            System.out.println("Postfix expression: " + convert(infix));
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

Compile and run it

javac InfixToPostfix.java
java InfixToPostfix

Java 11 or newer is needed for Map.of and Set.of. On Java 8, replace those two calls with a HashMap populated in a static block.

Sample output

Enter an infix expression: A+B*C
Postfix expression: A B C * +

Enter an infix expression: (A+B)*C-D
Postfix expression: A B + C * D -

Enter an infix expression: rate * 12 + 250
Postfix expression: rate 12 * 250 +

Enter an infix expression: (A+B
Error: unbalanced brackets: a missing ')'

Those are the real outputs of the listing above. Run the same expressions through the converter on the homepage to compare against the full stack trace.

How it works

The program is one class with four pieces: a precedence map, an associativity set, a convert method running the shunting-yard algorithm, and a main that reads a line.

What each part of the Java program does
PartPurpose
PRECEDENCEMaps each operator to a number; containsKey doubles as the "is this an operator?" test
RIGHT_ASSOCIATIVEA set holding only ^
stack.push()Push
stack.pop()Pop
stack.peek()Peek — reads the top without removing it
StringBuilderBuilds the output in linear time
Character.isLetterOrDigitIdentifies operands, including multi-character ones

Why ArrayDeque and not Stack

Use ArrayDeque. java.util.Stack extends Vector and synchronises every method, which costs performance in single-threaded code and is the reason the Java documentation recommends Deque instead.

Stack options in Java
TypePush / pop / peekVerdict
ArrayDeque<Character>O(1), unsynchronisedUse this
java.util.Stack<Character>O(1), synchronisedWorks, but legacy
LinkedList<Character>O(1), one node per elementMore allocation than needed
char[] with an int topO(1), no boxingFastest, closest to the C version

Swapping ArrayDeque for java.util.Stack needs no other edit — push, pop, peek and isEmpty have the same names on both. Note that Stack.peek throws EmptyStackException on an empty stack while ArrayDeque.peek returns null, so keep the isEmpty guard either way.

The two rules that decide correctness

Whether this program is right or wrong lives in one while condition.

the condition that matters
while (!stack.isEmpty()
        && stack.peek() != '('
        && (precedence(stack.peek()) > precedence(c)
            || (precedence(stack.peek()) == precedence(c)
                && !RIGHT_ASSOCIATIVE.contains(c)))) {
    output.append(stack.pop()).append(' ');
}
stack.push(c);
  • The == branch. Equal precedence must also pop, for left-associative operators. Remove it and A - B - C becomes A B C - -, which means A - (B - C).
  • The !RIGHT_ASSOCIATIVE.contains(c) guard. Without it, a ^ b ^ c becomes a b ^ c ^ instead of a b c ^ ^.
  • The stack.peek() != '(' guard. An opening bracket is a floor; popping must never run past it.

Evaluating the postfix result

To evaluate the postfix output, use a second stack holding Double values: push operands, and on each operator pop two values, apply it, push the result.

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

/** Evaluates a postfix expression with a stack of values. */
public class PostfixEvaluator {

    public static double evaluate(String postfix) {
        Deque<Double> stack = new ArrayDeque<>();

        for (String token : postfix.trim().split("\\s+")) {
            if (token.length() == 1 && "+-*/%^".indexOf(token.charAt(0)) >= 0) {
                if (stack.size() < 2) {
                    throw new IllegalArgumentException("not enough operands for '" + token + "'");
                }
                double right = stack.pop();          // popped first
                double left = stack.pop();           // popped second
                stack.push(apply(token.charAt(0), left, right));
            } else {
                stack.push(Double.parseDouble(token));
            }
        }

        if (stack.size() != 1) {
            throw new IllegalArgumentException("malformed postfix expression");
        }
        return stack.pop();
    }

    private static double apply(char op, double left, double right) {
        return switch (op) {
            case '+' -> left + right;
            case '-' -> left - right;
            case '*' -> left * right;
            case '/' -> {
                if (right == 0) throw new ArithmeticException("division by zero");
                yield left / right;
            }
            case '%' -> {
                if (right == 0) throw new ArithmeticException("modulo by zero");
                yield left % right;
            }
            case '^' -> Math.pow(left, right);
            default -> throw new IllegalArgumentException("unknown operator " + op);
        };
    }

    public static void main(String[] args) {
        String[] samples = {
            "2 + 3 * 4", "(2 + 3) * 4", "2 ^ 3 ^ 2",
            "100 / 5 / 2", "3 + 4 * 2 / (1 - 5) ^ 2", "6*4+2^5-3"
        };
        for (String infix : samples) {
            String postfix = InfixToPostfix.convert(infix);
            System.out.printf("%-26s -> %-26s = %s%n", infix, postfix, trim(evaluate(postfix)));
        }
    }

    private static String trim(double v) {
        return v == Math.floor(v) && !Double.isInfinite(v)
                ? String.valueOf((long) v)
                : String.valueOf(v);
    }
}
2 + 3 * 4                  -> 2 3 4 * +                  = 14
(2 + 3) * 4                -> 2 3 + 4 *                  = 20
2 ^ 3 ^ 2                  -> 2 3 2 ^ ^                  = 512
100 / 5 / 2                -> 100 5 / 2 /                = 10
3 + 4 * 2 / (1 - 5) ^ 2    -> 3 4 2 * 1 5 - 2 ^ / +      = 3.5
6*4+2^5-3                  -> 6 4 * 2 5 ^ + 3 -          = 53
Note the operand order

right is popped before left. The second value popped is the left operand, because it was pushed first. Swap those two lines and addition still works while subtraction, division and exponentiation silently invert — 2 3 2 ^ ^ would give 81 instead of 512.

Errors you are likely to hit

Common problems in an infix to postfix Java program
SymptomCauseFix
NullPointerExceptionpeek() returned null on an empty deque, then unboxedGuard with !stack.isEmpty() first
EmptyStackExceptionUsing java.util.Stack.peek without the guardSame guard
A-B-C gives A B C - -Equal precedence pushes instead of poppingAdd the == branch
a^b^c gives a b ^ c ^^ treated as left associativeAdd the RIGHT_ASSOCIATIVE guard
12+3 gives 1 2 3 +Reading one character per operandConsume the whole alphanumeric run
Slow on long inputBuilding output with String +=Use StringBuilder
char compared with ==Comparing Character objects instead of charUnbox first, or compare with char literals

Frequently asked questions

How do you convert infix to postfix in Java?

Use a Deque as the stack. Scan the expression left to right: append operands to the output, push an opening bracket, and on a closing bracket pop to the output until the matching opening bracket. For an operator, pop while the stack top has higher precedence - or equal precedence and the incoming operator is left associative - then push it. Pop whatever remains when the scan ends.

Should I use Stack or Deque in Java?

Use ArrayDeque. java.util.Stack extends Vector and synchronises every method, which costs performance and is unnecessary for single-threaded code. The Java documentation itself recommends Deque over Stack. ArrayDeque.push, pop and peek all run in constant time.

Why does peek() return null instead of throwing?

ArrayDeque.peek returns null on an empty deque, while element() throws NoSuchElementException. The code on this page guards every peek with !stack.isEmpty(), so the null case never arises. Comparing a null Character to a char would throw a NullPointerException during unboxing.

How do I handle multi-digit numbers in Java?

Consume the whole run of letters, digits, underscores and dots as one token instead of reading a single character. The convert method on this page does that with an inner while loop, so rate * 12 + 250 produces rate 12 * 250 + rather than splitting the digits apart.

What is the time complexity of the Java implementation?

O(n) time and O(n) space for n tokens. Each character is read once and each operator is pushed and popped at most once. Using StringBuilder rather than string concatenation keeps the output building linear too; concatenating with + inside the loop would make it O(n squared).

How do I evaluate the postfix expression in Java?

Use a second stack holding Double values. Push each operand, and on each operator pop two values, apply the operator with the second value popped as the left operand, and push the result. One value remains at the end. The PostfixEvaluator class on this page does exactly that.