DevNotes
Support
Collection Framework · Chapter 12

Deque Interface

The double-ended queue that is both a Queue and a Stack — method families, ArrayDeque's circular array and why it replaced Stack.

◷ 9 min read§ 19 sections? 14 interview questions{ } 15 code examples

#Introduction

Deque is one of the most useful interfaces in the Collections Framework because it can act as both a Queue and a Stack.

The name comes from Double Ended Queue. It allows insertion and removal from both ends.

#What is Deque?

Package:

Java
java.util.Deque
  • Iterable
    • Collection
      • Queue
        • Deque
          • ArrayDeque
          • LinkedList
For most normal single-threaded use cases, ArrayDeque is the preferred implementation

#Core Idea

A normal Queue: insert at rear, remove from front.

A Deque: insert/remove from either end.

Front
A
B
C
D
Rear
Front ⇄ … ⇄ Rear

Therefore Deque = Queue behaviour + Stack behaviour.

#Deque as a Queue

Java
Deque<Integer> deque = new ArrayDeque<>();

deque.offerLast(10);
deque.offerLast(20);
deque.offerLast(30);

deque.pollFirst();   // returns 10
  • offerLast() → insertion at rear
  • pollFirst() → removal from front

This gives normal FIFO behaviour.

#Deque as a Stack

Java
Deque<Integer> stack = new ArrayDeque<>();

stack.push(10);
stack.push(20);
stack.push(30);
30top
20
10
stack.pop() returns 30 — LIFO behaviour

#Why Deque is Better Than Stack

Java
// Legacy
Stack<Integer> stack = new Stack<>();

// Modern
Deque<Integer> stack = new ArrayDeque<>();

Why prefer Deque?

  • Stack inherits from legacy Vector
  • Vector synchronizes operations
  • ArrayDeque avoids unnecessary synchronization overhead
  • Deque provides a cleaner abstraction for stack operations

#Deque Method Families

This is the most important part to memorize.

OperationFirst end — throws exceptionFirst end — special valueLast end — throws exceptionLast end — special value
InsertaddFirst(e)offerFirst(e)addLast(e)offerLast(e)
RemoveremoveFirst()pollFirst()removeLast()pollLast()
InspectgetFirst()peekFirst()getLast()peekLast()

Just like Queue:

  • add… → exception on failure; offer… → special-value (false) failure
  • remove… → exception if empty; poll… → null if empty
  • get… → exception if empty; peek… → null if empty

Easy Mental Model

[0]
10
[1]
20
[2]
30
[3]
40
FRONT → REAR
OperationResult
addFirst(5)5 10 20 30 40
addLast(50)10 20 30 40 50
pollFirst()removes 10
pollLast()removes 40

#ArrayDeque

ArrayDeque is the most important Deque implementation.

Package:

Java
java.util.ArrayDeque

Declaration:

Java
public class ArrayDeque<E>
        extends AbstractCollection<E>
        implements Deque<E>,
                   Cloneable,
                   Serializable

It is:

  • Resizable
  • Array-backed
  • Not synchronized
  • Does not permit null
  • Efficient at both ends

Internal Structure

Unlike LinkedList, ArrayDeque does not create a Node object for every element. It uses an internal array and maintains positions indicating where elements should be inserted/removed.

[0]
null
[1]
A (head)
[2]
B
[3]
C
[4]
D
[5]
null
[6]
null
head points at A, tail at the next free slot after D

Why ArrayDeque Is Efficient

Adding/removing from either end generally requires only adjusting internal positions. There is no need to shift all elements, as would happen when inserting/removing at the beginning of an ArrayList.

OperationComplexity
addFirst() / addLast()O(1) amortized
pollFirst() / pollLast()O(1)
peekFirst() / peekLast()O(1)

Circular Buffer Concept

A useful mental model for ArrayDeque is a circular array. Suppose the array reaches its end — [A][B][C][D][ ][ ]. The next insertion doesn't necessarily need to move everything; the logical positions can wrap around: [ ][ ][C][D][A][B].

The implementation uses modular/wraparound indexing to treat the array as circular. When the array becomes full, it grows.

Why a Circular Structure?

Without circular indexing, repeatedly removing from the front could create unused space:

[A][B][C][D][ ][ ]
[ ][ ][C][D][ ][ ]
Instead of shifting C and D, ArrayDeque moves its logical front position

This keeps end operations efficient.

ArrayDeque Growth

ArrayDeque is dynamically resizable. When there is insufficient space:

Current array
Create larger array
Copy elements
Continue operation

Occasional resizing costs O(n), while ordinary insertion remains amortized O(1).

Why Doesn't ArrayDeque Allow null?

Java
Deque<String> deque = new ArrayDeque<>();

deque.offer(null);
Output
NullPointerException

Why? Because Deque methods such as peek() and poll() use null to represent "no element available". Allowing null would make the result ambiguous.

#ArrayDeque vs LinkedList

Both implement Deque, but their internal structures differ: ArrayDeque → resizable array; LinkedList → doubly linked nodes.

FeatureArrayDequeLinkedList
StructureArray-basedDoubly linked
End operationsO(1) amortizedO(1)
Memory overheadLowerHigher
Cache localityBetterWorse
Null❌ Not allowed✅ Allowed
Thread-safe❌ No❌ No
Typical Deque choice✅ UsuallyUsually not

For a pure Queue/Deque workload, prefer ArrayDeque unless you have a specific reason to use LinkedList.

#Deque vs Stack

FeatureDequeStack
APIModernLegacy
Queue behaviour✅ Yes❌ No
Stack behaviour✅ Yes✅ Yes
Double-ended operations✅ Yes❌ No
SynchronizationDepends on implementationVector-based synchronization
Preferred for new code✅ Yes❌ No

#Stack and Queue Operations Using Deque

Stack (LIFO)

Java
Deque<Integer> stack = new ArrayDeque<>();
Stack methodEquivalent Deque method
push()addFirst()
pop()removeFirst()
peek()peekFirst()

Queue (FIFO)

Use offerLast(), pollFirst() and peekFirst():

Java
Deque<Integer> queue = new ArrayDeque<>();

queue.offerLast(10);
queue.offerLast(20);
queue.offerLast(30);

System.out.println(queue.pollFirst());
Output
10

Deque as Both

  • Deque
    • Queue — FIFOofferLast() · pollFirst()
    • Stack — LIFOpush() · pop()
One Deque can support either behaviour — one of its biggest advantages

#More Deque Operations

descendingIterator()

Java
Deque<Integer> deque = new ArrayDeque<>();

deque.addLast(10);
deque.addLast(20);
deque.addLast(30);

Iterator<Integer> it = deque.descendingIterator();
Output
30
20
10

Useful when you need reverse traversal.

removeFirstOccurrence() and removeLastOccurrence()

Java
deque.removeFirstOccurrence(20);   // removes the first matching occurrence
deque.removeLastOccurrence(20);    // removes the last matching occurrence

These operations require searching and are therefore generally O(n).

Deque Does Not Provide Random Access

You cannot do deque.get(3); like an ArrayList. Deque is designed around the front + rear, not arbitrary indexes.

#Time Complexity

For ArrayDeque:

OperationComplexity
addFirst() / addLast()O(1) amortized
offerFirst() / offerLast()O(1) amortized
removeFirst() / removeLast()O(1)
pollFirst() / pollLast()O(1)
peekFirst() / peekLast()O(1)
contains()O(n)
remove(Object)O(n)
IterationO(n)

#Real-World Use Cases

Stack

  • Expression evaluation — infix → postfix
  • Balanced parentheses — ( [ { } ] )
  • Undo operations — Action 1, Action 2, Action 3 pushed onto a stack

Queue

Task processing: Request → Deque → Worker.

Sliding Window Algorithms

Deque is extremely useful for problems such as "find the maximum value in every window of size K". A monotonic deque can solve this efficiently — a common coding-interview application.

Palindrome Checking

Compare front ↔ rear using pollFirst() and pollLast().

#Interview Scenarios

Q1Implement a stack without using Stack
Java
Deque<Integer> stack = new ArrayDeque<>();

stack.push(10);
stack.push(20);
stack.push(30);
stack.pop();   // 30
Q2Implement a FIFO Queue using Deque
Java
Deque<Integer> queue = new ArrayDeque<>();

queue.offerLast(10);
queue.offerLast(20);
queue.pollFirst();   // 10
Q3Insert/remove from both ends efficiently
Java
Deque<Integer> deque = new ArrayDeque<>();
Q4Coding pattern: reverse a sequence using Deque
Java
Deque<Integer> deque = new ArrayDeque<>();

deque.addLast(10);
deque.addLast(20);
deque.addLast(30);

while (!deque.isEmpty()) {
    System.out.println(deque.pollLast());
}
Output
30
20
10

The key operation is pollLast().

#Common Interview Traps

ClaimVerdict
"Deque is only a Queue."❌ Wrong — it supports both Queue and Stack semantics.
"ArrayDeque allows null."❌ Wrong — it rejects null.
"ArrayDeque is thread-safe."❌ Wrong — it is not synchronized.
"LinkedList is always better for Deque because insertion/removal is O(1)."⚠️ Incomplete — ArrayDeque also has efficient end operations and usually better memory locality and lower per-element overhead.
"ArrayDeque grows exactly like ArrayList."❌ Wrong — don't assume ArrayList's ~1.5× growth formula applies.

#Frequently Asked Interview Questions

Q1What is Deque?

A double-ended queue that allows insertion and removal from both ends.

Q2Can Deque act as a Stack?

Yes — use push(), pop() and peek().

Q3Can Deque act as a Queue?

Yes — for example offerLast(), pollFirst() and peekFirst().

Q4Why is ArrayDeque preferred over Stack?

Because Stack is a legacy Vector-based class with synchronization overhead, while ArrayDeque is designed for efficient deque/stack operations.

Q5Why doesn't ArrayDeque allow null?

Because null is used as the special return value by methods such as poll() and peek() when the deque is empty.

Q6Is ArrayDeque thread-safe?

No.

Q7ArrayDeque vs LinkedList?

ArrayDeque is generally preferred for pure Queue/Deque workloads because it is array-backed, has lower per-element overhead, and typically has better cache locality.

Q8What is the complexity of addFirst/addLast?

O(1) amortized.

Q9Can ArrayDeque perform random access?

No.

Q10What is descendingIterator()?

It returns an iterator that traverses the Deque from rear to front.

#Decision Framework

NeedUseMethods
Double-ended operationsDeque → ArrayDequeaddFirst/Last, pollFirst/Last
StackDeque → ArrayDequepush / pop / peek
FIFO QueueDeque → ArrayDequeofferLast / pollFirst
Concurrent producer-consumerDon't use ArrayDeque → BlockingQueueput / take

Final Mental Model

  • Dequeadd/remove at front and rear
    • ArrayDeque
      • Queue — FIFOofferLast() · pollFirst() · peekFirst()
      • Stack — LIFOpush() · pop() · peek()

#Chapter Summary

ConceptKey Point
DequeDouble-ended Queue
InsertBoth ends
RemoveBoth ends
Queue behaviourFIFO
Stack behaviourLIFO
Preferred implementationArrayDeque
ArrayDeque structureResizable array
End operationsO(1) amortized
Random accessNo
NullNot allowed
Thread-safeNo
Reverse traversaldescendingIterator()
Main advantageEfficient operations at both ends

Interview Readiness Checklist

  • What is Deque?
  • Why is it called a double-ended Queue?
  • How can Deque implement a Stack?
  • How can Deque implement a Queue?
  • addFirst() vs offerFirst()?
  • removeFirst() vs pollFirst()?
  • getFirst() vs peekFirst()?
  • Why is ArrayDeque preferred over Stack?
  • ArrayDeque vs LinkedList?
  • Why doesn't ArrayDeque allow null?
  • Is ArrayDeque thread-safe?
  • What is the circular-array concept?
  • What is descendingIterator()?
  • What are removeFirstOccurrence() and removeLastOccurrence()?
  • What is the complexity of operations at both ends?