Why can't I loop over a Stack using a for loop?

Viewed 61

I have a Stack with 3 items and I want to loop over each item. If I do this:

public class Main {
    public static void main(String[] args) {
        Stack<Integer> s = new Stack<>();
        s.add(1);
        s.add(2);
        s.add(3);
    
        for (Integer num = s.pop(); !s.isEmpty(); num = s.pop()) {
            System.out.println(num);
        }
    }
}

then it only prints out 3 and 2 but not 1. Why is that?

3 Answers

The for loop pops the stack then exits the loop if the stack is empty.

for (Integer num = s.pop(); !s.isEmpty(); num = s.pop()) {
    System.out.println(num);
}

Put another way, num = s.pop() is run before the test !s.isEmpty(). So on the final iteration, the stack is empty, therefore the loop body isn't executed.

There are many different ways around this. One thing you could do is use a while loop instead:

while (!s.isEmpty()) {
    System.out.println(s.pop());
}

For loop consists of 3 expressions: initialization, termination, increment:

for (initialization; termination; increment) {
    //
}
  1. initialization is executed only once
  2. termination is executed each iteration
  3. increment is executed each iteration

In your case you retrieve from the stack twice on the first iteration, hence your problem with a non-printing element. You might be wondering why does it print 3,2 and not 2,1? That's because increment expression is invoked after (in the very end) each iteration through the loop.

All of sections are optional, so you can iterate this way:

for (; ; ) {
    System.out.println(s.pop());
}

... and you will eventually have java.util.EmptyStackException on an attempt to pop an element from already empty stack.

So the most basic way to iterate over a stack with a for loop is to make use of termination statement as a "safe-check":

for (; !s.isEmpty(); ) {
    System.out.println(s.pop());
}

... which is basically a more complex and counterintuitive way to write a while-loop:

while (!s.isEmpty()) {
    System.out.println(s.pop());
}

Docs: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html

The reason is that the stack is already empty, when the condition is evaluated.

Few options(not all) how to correctly pop and print all items:

  1. Do-while loop. As mentioned in comments, will throw EmptyStackException, if the stack is initially empty.
do {
  System.out.println(s.pop());
} while (!s.empty());
  1. While loop.
while (!s.empty()) {
  System.out.println(s.pop());
}
Related