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.
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.
| Part | Purpose |
|---|---|
| PRECEDENCE | Maps each operator to a number; containsKey doubles as the "is this an operator?" test |
| RIGHT_ASSOCIATIVE | A set holding only ^ |
| stack.push() | Push |
| stack.pop() | Pop |
| stack.peek() | Peek — reads the top without removing it |
| StringBuilder | Builds the output in linear time |
| Character.isLetterOrDigit | Identifies 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.
| Type | Push / pop / peek | Verdict |
|---|---|---|
| ArrayDeque<Character> | O(1), unsynchronised | Use this |
| java.util.Stack<Character> | O(1), synchronised | Works, but legacy |
| LinkedList<Character> | O(1), one node per element | More allocation than needed |
| char[] with an int top | O(1), no boxing | Fastest, 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.
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 andA - B - CbecomesA B C - -, which meansA - (B - C). -
The
!RIGHT_ASSOCIATIVE.contains(c)guard. Without it,a ^ b ^ cbecomesa b ^ c ^instead ofa 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.
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
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
| Symptom | Cause | Fix |
|---|---|---|
| NullPointerException | peek() returned null on an empty deque, then unboxed | Guard with !stack.isEmpty() first |
| EmptyStackException | Using java.util.Stack.peek without the guard | Same guard |
A-B-C gives A B C - - | Equal precedence pushes instead of popping | Add the == branch |
a^b^c gives a b ^ c ^ | ^ treated as left associative | Add the RIGHT_ASSOCIATIVE guard |
12+3 gives 1 2 3 + | Reading one character per operand | Consume the whole alphanumeric run |
| Slow on long input | Building output with String += | Use StringBuilder |
| char compared with == | Comparing Character objects instead of char | Unbox first, or compare with char literals |
Frequently asked questions
How do you convert infix to postfix in Java?
Use a Deque
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.
Related
std::stack, plus a postfix evaluator using fmod and pow.
Check your outputRun the same expressions through the converter and compare the stack trace.
The same converter in CAn array stack and a linked-list variant, both compile-tested.
The same converter in PythonA list as the stack, a step-by-step trace and a postfix evaluator.