-2

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?

DVT
  • 115
  • 1
  • 9
  • 1
    After last pop it's empty and doesn't iterate over that value. – K.Nicholas Sep 15 '22 at 14:04
  • When you pop the last number, `s.isEmpty()` becomes true, so the for-loop finishes without printing the last number. – khelwood Sep 15 '22 at 14:06
  • 1
    @K.Nicholas @khelwood So a for loop executes the third statement (`num = s.pop()` in this case) and then checks the condition (`!s.isEmpty()`)? – DVT Sep 15 '22 at 14:08

3 Answers3

1

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());
}
cba
  • 166
  • 1
  • 5
  • So the order in which a for loop executes its statements is: 1st statement at the start, runs, then loops executing the 3rd statement, checking condition, and running? – DVT Sep 15 '22 at 14:13
  • @DVT you are correct. There are some neat flow charts out there which explain it a bit better, e.g. https://www.geeksforgeeks.org/java-for-loop-with-examples/ – cba Sep 15 '22 at 14:21
1

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

star67
  • 1,505
  • 7
  • 16
0

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());
}
Chaosfire
  • 4,818
  • 4
  • 8
  • 23
  • 3
    Note: the do-while loop will work in this scenario, but would result in an `EmptyStackException` if the stack is empty to begin with. – cba Sep 15 '22 at 14:16