Deque (Double-Ended Queue)
About
A Deque (Double-Ended Queue) is a linear data structure in Java that allows the addition and removal of elements from both ends (head and tail). It is part of the java.util
package and is an interface, with common implementations such as ArrayDeque
and LinkedList
. Deques can function as both stacks (LIFO) and queues (FIFO), making them versatile.
Features
Bi-Directional Access: Elements can be added or removed from both ends, providing more flexibility than a regular queue.
Stack-Like Operations: Functions as a stack when elements are added/removed from the same end.
Queue-Like Operations: Functions as a regular queue when operations are restricted to one end for addition and the other for removal.
Non-Capacity Bound (default): Most implementations of
Deque
are unbounded, except forArrayBlockingQueue
.No Null Elements: Null values are not allowed in
Deque
implementations likeArrayDeque
(avoids ambiguity in certain operations).Thread-Safe Variants: The
Deque
interface has thread-safe implementations likeLinkedBlockingDeque
in thejava.util.concurrent
package.Custom Ordering: Specialized implementations like
PriorityBlockingDeque
allow priority-based ordering.Highly Efficient: Both head and tail operations are optimized for performance in most
Deque
implementations.
Key Methods
Method Type
Method
Description
Add Operations
addFirst(E e)
Inserts the element at the front of the deque.
addLast(E e)
Inserts the element at the rear of the deque.
offerFirst(E e)
Adds an element at the front without throwing an exception if the deque is full.
offerLast(E e)
Adds an element at the rear without throwing an exception if the deque is full.
Remove Operations
removeFirst()
Removes and returns the first element. Throws an exception if the deque is empty.
removeLast()
Removes and returns the last element. Throws an exception if the deque is empty.
pollFirst()
Retrieves and removes the first element, or returns null
if the deque is empty.
pollLast()
Retrieves and removes the last element, or returns null
if the deque is empty.
Access Operations
getFirst()
Retrieves the first element without removing it. Throws an exception if the deque is empty.
getLast()
Retrieves the last element without removing it. Throws an exception if the deque is empty.
peekFirst()
Retrieves the first element without removing it; returns null
if empty.
peekLast()
Retrieves the last element without removing it; returns null
if empty.
Iterators
descendingIterator()
Returns an iterator that traverses elements in reverse order.
Deque Implementations in java.util
java.util
1. ArrayDeque
Description: A resizable array implementation of the
Deque
interface. It allows the addition and removal of elements from both ends.Use Case: Suitable for scenarios where frequent additions and removals are needed from both ends of the queue, such as for implementing a sliding window or a double-ended stack.
Features:
Non-thread-safe (not synchronized).
Dynamic resizing with amortized constant-time complexity for adding/removing elements from both ends.
Does not support null elements.
2. LinkedList
Description: A doubly-linked list implementation of the
Deque
interface. It supports insertion and removal of elements from both ends.Use Case: Suitable for use cases where a linked list structure is preferred (i.e., frequent insertions/removals), such as implementing deques, stacks, and queues.
Features:
Thread-unsafe.
Efficient for adding/removing elements from both ends (O(1) time complexity).
Allows null elements.
Can be used as both a stack and queue (FIFO/LIFO).
Deque Implementations in java.util.concurrent
java.util.concurrent
1. ConcurrentLinkedDeque
Description: A non-blocking, lock-free, thread-safe implementation of a
Deque
. This implementation uses an optimistic concurrency mechanism.Use Case: Ideal for multi-threaded environments where elements need to be added/removed from both ends in a highly concurrent, non-blocking manner, such as for producer-consumer systems.
Features:
Thread-safe and non-blocking.
Ideal for concurrent applications that require high throughput and low contention.
Does not support blocking operations.
2. LinkedBlockingDeque
Description: A thread-safe, optionally bounded blocking deque. This implementation supports both blocking and non-blocking operations.
Use Case: Suitable for bounded capacity queues in concurrent programming, where threads can block until space becomes available or an element becomes available.
Features:
Supports both blocking and non-blocking operations.
Can be bounded or unbounded, providing flexibility in capacity management.
Thread-safe with support for concurrent access.
3. DelayQueue
Description: An implementation of the
BlockingQueue
interface that supports scheduling of elements with an associated delay. It is a specialized form of a blocking deque.Use Case: Ideal for scenarios where tasks need to be delayed until a specific time, such as scheduling tasks or implementing time-based buffers.
Features:
Thread-safe and supports blocking operations.
Blocks until the delay for an element has elapsed, after which it can be removed.
Often used for scheduled task execution or time-based event handling.
4. PriorityBlockingDeque
Description: A thread-safe, blocking deque that supports elements with a priority order. This implementation behaves like a priority queue but allows insertion/removal from both ends.
Use Case: Useful when you need a deque with priorities and blocking capabilities, such as for managing tasks with varying urgency.
Features:
Thread-safe with blocking support.
Supports custom ordering of elements based on priority.
Elements are sorted based on priority and can be removed in order of priority.
Implementations Comparison Table
Implementation
Thread-Safe
Blocking Operations
Resizable
Features
When to use
ArrayDeque
No
No
Yes
Fast, resizable array, no null elements
Best for high-performance situations where we need to frequently add/remove elements from both ends and do not require thread safety.
LinkedList
No
No
Yes
Doubly-linked list, allows null elements
Use when we need a simple doubly linked list structure that supports both ends for adding/removing elements.
ConcurrentLinkedDeque
Yes
No
Yes
Lock-free, non-blocking, high concurrency
Ideal for high-concurrency scenarios where we need thread safety but do not require blocking operations.
LinkedBlockingDeque
Yes
Yes
Yes
Blocking, bounded, thread-safe
Perfect for use cases involving bounded blocking queues where threads may wait for space to become available or for elements to be processed.
DelayQueue
Yes
Yes
No
Time-based scheduling, thread-safe
Use this when we need to schedule elements with a delay or timeout, such as for time-based event handling.
PriorityBlockingDeque
Yes
Yes
Yes
Priority-based, thread-safe
Use when we need a priority queue with blocking capabilities, where elements can be processed in order of priority.
When to Use Deques ?
Double-Ended Operations: When both ends need frequent addition/removal.
Implementing Stacks: Use
Deque
instead ofStack
for a modern and efficient stack implementation.Sliding Window Problems: Deques are ideal for maintaining a window of elements during array traversal.
Task Scheduling: Manage tasks with varying priority by adding to specific ends of the deque.
Last updated
Was this helpful?