
ArrayDeque in Java
The ArrayDeque is a resizable array implementation of the `Deque` interface. It supports adding and removing elements from both ends of the deque (double-ended queue) efficiently.
July 24, 2024 · 5 min read
The ArrayDeque is a resizable array implementation of the Deque interface. It supports adding and removing elements from both ends of the deque (double-ended queue) efficiently.
Key Properties of ArrayDeque
The ArrayDeque can be used as a queue (FIFO) and a stack (LIFO).
It allows the insertion and removal of elements from both ends. The underlying array resizes dynamically as elements are added or removed.
It is not synchronized, meaning you need to manage synchronization externally for multithreaded use.
How it actually works
ArrayDeque is backed by a circular array — not a linked list of nodes, despite implementing the same Deque interface as LinkedList. It tracks a head and tail index into that array; adding to the front decrements head (wrapping around to the end when it hits 0), and adding to the end increments tail, wrapping the same way. The array's capacity is always kept a power of two specifically so that wraparound can be computed with a cheap bitmask (index & (capacity - 1)) instead of the more expensive modulo operator. When the array fills up, it doubles, same as Vector's default growth.
This circular-array design is exactly why ArrayDeque beats both LinkedList and Stack for stack/queue workloads: no per-element node allocation, and elements sit contiguously in memory for better cache locality, at the cost of losing LinkedList's ability to splice in O(1) at an arbitrary interior position.
No null elements allowed
Unlike LinkedList, ArrayDeque throws a NullPointerException if you try to add null. This isn't an arbitrary restriction — peek() and poll()-style methods return null to signal "the deque is empty," so allowing null as an actual element would make that signal ambiguous. If your existing code stores null in a LinkedList used as a queue, switching to ArrayDeque is not a drop-in replacement.
Its iterator is fail-fast, like the other non-concurrent collections in this course.
Example: Suppose you are developing a text editor with an undo and redo feature. You can use two ArrayDeque instances to manage the history of operations: one for undo and one for redo.
public class TextEditor {
private StringBuilder text;
private ArrayDeque<String> undoStack;
private ArrayDeque<String> redoStack;
public TextEditor() {
text = new StringBuilder();
undoStack = new ArrayDeque<>();
redoStack = new ArrayDeque<>();
}
public void appendText(String newText) {
undoStack.push(text.toString());
redoStack.clear();
text.append(newText);
}
public void undo() {
if (!undoStack.isEmpty()) {
redoStack.push(text.toString());
text = new StringBuilder(undoStack.pop());
}
}
public void redo() {
if (!redoStack.isEmpty()) {
undoStack.push(text.toString());
text = new StringBuilder(redoStack.pop());
}
}
public String getText() {
return text.toString();
}
public static void main(String[] args) {
TextEditor editor = new TextEditor();
editor.appendText("Hello");
editor.appendText(", World!");
System.out.println("Current Text: " + editor.getText());
editor.undo();
System.out.println("After Undo: " + editor.getText());
editor.redo();
System.out.println("After Redo: " + editor.getText());
editor.undo();
editor.undo();
System.out.println("After two Undos: " + editor.getText());
}
}Output:
Current Text: Hello, World!
After Undo: Hello
After Redo: Hello, World!
After two Undos:ArrayDeque vs the Alternatives
- vs
LinkedList:ArrayDequeis faster and lighter for the sameDeque/Queueoperations — no per-node allocation, better cache behaviour, and the JDK's own documentation recommends it overLinkedListfor this purpose.LinkedListonly wins if you neednullelements or O(1) insertion at an arbitrary interior position via aListIterator. - vs
Stack: see the Stack article —Stack extends Vector, inherits synchronized-by-default overhead most code doesn't need, and exposes a fullListAPI that can be used to accidentally break LIFO ordering.ArrayDequeavoids both problems. - vs
PriorityQueue: different job entirely —ArrayDequepreserves insertion order (whichever end you push/pop from);PriorityQueuereorders by priority. Don't reach forArrayDequeif what you actually need is "always process the smallest/largest next."
