Queue, Deque and ArrayDeque
Queue, Deque and ArrayDeque: method families, FIFO and stack usage, circular-array internals, BFS, monotonic deques and when to choose BlockingQueue.
On this page
- Picture it first: a circular waiting line
- Queue under the hood: two method families
- Deque as both queue and stack
- ArrayDeque internals: head, tail and growth
- BFS: the queue pattern everyone should know
- Sliding window maximum: the monotonic deque
- Choosing between ArrayDeque, LinkedList and BlockingQueue
- How to say it in the interview
🚦 What are Queue, Deque and ArrayDeque?
A Queue is a collection where removal follows a policy, most often FIFO: first in, first out. A Deque is a double-ended queue, so you can add or remove at both the front and the back. ArrayDeque is the usual in-memory implementation, backed by a circular Object[], like a ticket counter with a front door, a back door and a belt that wraps around.
For this lesson, we'll focus on the Queue method families, Deque as both queue and stack, ArrayDeque internals, why it usually beats Stack and LinkedList, and the interview patterns you should write from memory: FIFO scheduling, BFS and a monotonic deque for sliding windows. We'll leave priority ordering to PriorityQueue and binary heaps and thread coordination to Blocking and concurrent queues at a glance.
Below the line (out of scope)
- Priority-based removal: PriorityQueue and binary heaps
- Producer-consumer queues for threads: Blocking and concurrent queues at a glance
- Stack interview patterns in more depth: Stack algorithms with ArrayDeque
- Iteration failure modes: Fail-fast vs fail-safe iterators
The 30-second answer
Queue has two families: add, remove, element throw when they cannot complete; offer, poll, peek return a special value such as false or null. Deque extends that idea to both ends: addFirst, removeFirst, peekFirst, and the same for the last end. Its stack methods map to the front: push is addFirst, pop is removeFirst, and peek is peekFirst. ArrayDeque stores elements in a circular Object[] with head pointing at the first element and tail at the next empty slot. It rejects null because poll uses null to mean empty, is not thread-safe, and has fail-fast iterators. For single-threaded queues and stacks, prefer ArrayDeque over legacy Stack and usually over LinkedList; if producers and consumers are separate threads and must wait, use a BlockingQueue.
What you’ll learn
- Separate the two
Queuemethod families: throwing (add,remove,element) and special-value (offer,poll,peek) - Use
Dequeas both a FIFO queue and a LIFO stack, withpush,popandpeekmapped to the front - Explain
ArrayDequeinternals: circularObject[],head,tail, nonull, growth and fail-fast iterators - Choose
ArrayDequeover legacyStackand usually overLinkedListfor single-threaded queues and stacks - Recognize when a FIFO producer-consumer prompt needs a
BlockingQueueinstead ofArrayDeque - Write BFS with a queue and sliding-window maximum with a monotonic deque
Interview radar
Asked very often: Which queue method returns null instead of throwing, and why is ArrayDeque preferred over Stack?
Core follow-up: How does a circular array avoid shifting elements on every poll?
Production angle: A job scheduler prompt says FIFO, but if it also says producers wait when the queue is full, the answer changes from ArrayDeque to BlockingQueue.
01Picture it first: a circular waiting line
Think of an ArrayDeque as seats arranged in a circle, not a straight line. head marks the next person to serve. tail marks the next empty seat where a new person joins. When either marker reaches the end of the array, it wraps back to slot 0.
The trick is no shifting. Removing the front element clears one slot and moves head; adding at the back writes one slot and moves tail.
ArrayDeque circular buffer with an Object array, head pointing to the first element, tail pointing to the next empty slot, and wrapped live elements occupying the end and beginning of the array
Notice the empty slot at tail. Internally, ArrayDeque keeps enough space to distinguish full from empty and grows when an add would make the markers collide. It does not store null elements, which lets poll, peek and empty slots all use null safely.
02Queue under the hood: two method families
The Queue interface gives every core operation two names. One family throws on failure. The other returns a special value. The right choice depends on whether failure is exceptional or expected.
| Operation | Throws on failure | Returns special value |
|---|---|---|
| Insert | add(e) |
offer(e) returns false |
| Remove head | remove() |
poll() returns null |
| Inspect head | element() |
peek() returns null |
For unbounded ordinary queues, add and offer usually both succeed. The difference matters for bounded queues such as ArrayBlockingQueue, where offer can say false instead of throwing.
Try it yourself first
Before reading on, predict which three lines throw on an empty queue and which two print null.
Queue<String> jobs = new ArrayDeque<>();
System.out.println(jobs.peek()); // null
System.out.println(jobs.poll()); // null
// jobs.element(); // NoSuchElementException
// jobs.remove(); // NoSuchElementException
jobs.offer("reindex");
jobs.add("email-digest");
System.out.println(jobs.remove()); // reindex
System.out.println(jobs.poll()); // email-digest
Notice three things:
peekandpollcan returnnull, soArrayDequeforbids storingnullelements.offerreads better thanaddwhen queue capacity might matter.removeandelementare fine when an empty queue means your program has a bug.
The scenario version shows up often: "You are scheduling jobs FIFO. Which collection do you use?" If this is single-threaded or externally synchronized, I use ArrayDeque<Job> and call offer and poll. If the prompt says producers must wait when the queue is full and consumers must wait when it is empty, I switch to a bounded BlockingQueue, usually ArrayBlockingQueue or LinkedBlockingQueue.
I have seen a background worker use LinkedList as a queue because the team remembered "linked list removes from the front cheaply". The queue held a few thousand jobs, and most CPU samples were not in business logic; they were allocation and pointer chasing. Replacing it with ArrayDeque removed one node allocation per job and made the worker's memory profile boring again.
03Deque as both queue and stack
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- ArrayDeque internals: head, tail and growth
- BFS: the queue pattern everyone should know
- Sliding window maximum: the monotonic deque
- Choosing between ArrayDeque, LinkedList and BlockingQueue
- How to say it in the interview
From ₹199 · one-time payment · No auto-renewal