Java Deque Interface
The Deque
(short for Double-Ended Queue) is a linear data structure in Java that allows elements to be added or removed from both ends. It is an extension of the Queue
interface in the Java Collections Framework, offering more flexibility than a regular queue or stack. With a Deque
, you can treat it as both a queue (FIFO) and a stack (LIFO) depending on how you add or remove elements.
The Deque
interface is part of the java.util
package and extends the Queue
interface. It represents a double-ended queue, allowing for the insertion and removal of elements at both the front and the back of the queue. This makes it more versatile than a traditional queue, where elements are only added at the rear and removed from the front.
Unlike a standard queue, which follows the First-In-First-Out (FIFO) order, a Deque
can operate as a FIFO queue, a Last-In-First-Out (LIFO) stack, or a combination of both. This makes the Deque
a powerful data structure for various use cases.
The Deque
interface provides several methods for working with elements from both ends of the queue:
true
if successful.true
if successful.null
if the deque is empty.null
if the deque is empty.null
if the deque is empty.null
if the deque is empty.Several classes implement the Deque
interface, each offering different characteristics based on the underlying data structure:
ArrayDeque: A resizable array implementation of the Deque
interface. It is faster than LinkedList
for most use cases and supports adding and removing elements from both ends efficiently.
LinkedList: A doubly linked list that implements both the Deque
and List
interfaces. It is slower than ArrayDeque
in most cases but provides additional list-related functionality.
ConcurrentLinkedDeque: A thread-safe version of Deque
for use in multi-threaded environments. It is part of the java.util.concurrent
package.
Let’s look at practical examples using the Deque
interface with some common implementations.
ArrayDeque
is one of the most commonly used implementations of the Deque
interface. It supports fast insertions and removals at both ends.
import java.util.ArrayDeque;
import java.util.Deque;
public class Main {
public static void main(String[] args) {
// Creating a Deque using ArrayDeque
Deque<String> deque = new ArrayDeque<>();
// Adding elements at both ends
deque.addFirst("First");
deque.addLast("Last");
// Displaying the deque
System.out.println("Deque: " + deque);
// Removing and displaying elements from both ends
System.out.println("Removed First: " + deque.removeFirst());
System.out.println("Removed Last: " + deque.removeLast());
// Checking the size of the deque
System.out.println("Deque size after removal: " + deque.size());
}
}
Output:
Deque: [First, Last]
Removed First: First
Removed Last: Last
Deque size after removal: 0
While ArrayDeque
is often preferred for better performance, LinkedList
also implements the Deque
interface and can be used when you need additional functionality from the List
interface.
import java.util.LinkedList;
import java.util.Deque;
public class Main {
public static void main(String[] args) {
// Creating a Deque using LinkedList
Deque<String> deque = new LinkedList<>();
// Adding elements at both ends
deque.offerFirst("Apple");
deque.offerLast("Banana");
// Displaying the deque
System.out.println("Deque: " + deque);
// Removing and displaying elements from both ends
System.out.println("Removed First: " + deque.pollFirst());
System.out.println("Removed Last: " + deque.pollLast());
// Checking the size of the deque
System.out.println("Deque size after removal: " + deque.size());
}
}
Output:
Deque: [Apple, Banana]
Removed First: Apple
Removed Last: Banana
Deque size after removal: 0
A Deque
can also be used as a stack, where elements are added and removed from the same end (the top of the stack).
import java.util.ArrayDeque;
import java.util.Deque;
public class Main {
public static void main(String[] args) {
// Creating a Deque using ArrayDeque
Deque<String> stack = new ArrayDeque<>();
// Pushing elements onto the stack (add to the front)
stack.push("Red");
stack.push("Green");
stack.push("Blue");
// Displaying the stack
System.out.println("Stack: " + stack);
// Popping elements from the stack (remove from the front)
System.out.println("Popped Element: " + stack.pop());
// Displaying the stack after pop
System.out.println("Stack after pop: " + stack);
}
}
Output:
Stack: [Blue, Green, Red]
Popped Element: Blue
Stack after pop: [Green, Red]
In this case, the Deque
behaves like a stack, where the last element added is the first to be removed (LIFO).
In its simplest form, a Deque
can also function as a queue, where elements are added at the end and removed from the front.
import java.util.ArrayDeque;
import java.util.Deque;
public class Main {
public static void main(String[] args) {
// Creating a Deque using ArrayDeque
Deque<String> queue = new ArrayDeque<>();
// Adding elements to the queue
queue.offer("One");
queue.offer("Two");
queue.offer("Three");
// Displaying the queue
System.out.println("Queue: " + queue);
// Removing elements from the front of the queue
System.out.println("Polled Element: " + queue.poll());
// Displaying the queue after poll
System.out.println("Queue after poll: " + queue);
}
}
Output:
Queue: [One, Two, Three]
Polled Element: One
Queue after poll: [Two, Three]
In this example, the Deque
behaves like a queue, following the FIFO (First-In-First-Out) principle.
The Deque
is ideal for the following use cases:
Deque
allows insertion and removal of elements at both ends, while a Queue
only allows access from one end (FIFO).Deque
can be used as a stack, but a Stack
allows only operations at the top (LIFO).LinkedList
can also function as a Deque
, but it is generally slower than ArrayDeque
for most operations.