As you probably remember, a Stack is an abstract data type where elements are inserted and removed according to the last-in-first-out (LIFO) principle. The simplest real-life example is a stack of books. Only a book placed at the top can be removed at a time, but a new book is always added on the top of the stack.
In the following example, we illustrate some operations of Deque using it as a stack.
Deque<String> stack = new ArrayDeque<>();
stack.offerLast("first");
stack.offerLast("second");
stack.offerLast("third");
System.out.println(stack); // [first, second, third]
System.out.println(stack.pollLast()); // third
System.out.println(stack.pollLast()); // second
System.out.println(stack.pollLast()); // first
System.out.println(stack.pollLast()); // null
As you can see, it really works. In addition, the ArrayDeque implementation is quite efficient for representing large stacks.
Let's take a look at different methods that are useful while working with the deque.
Push, Peek, and Pop methods
In the push(E e) method, the element is inserted at the beginning of the deque. It corresponds to the addFirst() method.
Deque<Integer> deque= new ArrayDeque<>();
deque.add(1);
deque.add(2);
deque.add(3);
deque.add(4);
System.out.println(deque);
deque.push(5);
System.out.println(deque);
The output of the following code will be:
[1, 2, 3, 4]
[5, 1, 2, 3, 4]
In the peek() method, the element that heads the deque is returned but not removed from the deque, or null is returned if the deque is empty.
System.out.println(deque.peek());
The result of this code is the following:
5
In the pop() method, the element that heads the deque is returned and removed from the deque. It is equivalent to the removeFirst() method.
System.out.println(deque.pop());
System.out.println(deque);
Here's what this code will output:
5
[1, 2, 3, 4]The old Stack class
Sometimes, the old Stack<E> class with a more minimalistic API can be found in legacy source code. It doesn't implement the Deque or Queue interfaces. Here is a simple example.
Stack<String> stack = new Stack<>();
stack.push("first");
stack.push("second");
stack.push("third");
System.out.println(stack); // [first, second, third]
System.out.println(stack.pop()); // "third"
System.out.println(stack.pop()); // "second"
System.out.println(stack.pop()); // "first"
System.out.println(stack.pop()); // throws EmptyStackException
The method pop() always throws an exception if the stack is empty.
Do not forget, according to the official Java documentation, it's preferable to use implementations of the Deque interface as stacks.
Using Deque instead of Stack
Using the Stack class in a single-threaded environment is inefficient, so it is best to use Deque for the stack as also prescribed by the official documentation. Also, Stack is a class and Deque is an interface. Where we can inherit only one class but can call multiple interfaces.
Additionally, stacks and deques have different reverse iteration orders. A deque returns an iterator over the elements in the proper order. The elements will be returned in order from first (head) to last (tail).
Stack class, but, according to the Java Doc, a more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class. So, it is recommended to use Deque for stacks.
Conclusion
We've considered the Deque, a subtype of the Queue interface. It is recommended to use Deque for stacks, as it is also prescribed by the official documentation. The ArrayDeque implementation is quite efficient for representing large stacks. There are three methods that are quite useful to work with deques: pop(), peek() and push(). Sometimes you can find an old Stack class in source code, but it is recommended to avoid it in new code.