Queue and Deque in Java: ArrayDeque and PriorityQueue
A Queue processes elements in an order it decides. A Deque works at both ends, and it is the right way to build a stack in Java.
-
Java Basics
- Introduction to Java
- Setting Up Java and Writing Your First Program
- Variables, Data Types and Literals in Java
- Type Casting and Type Conversion in Java
- Operators and Expressions in Java
- Input and Output in Java
- Comments, Keywords and Naming Conventions in Java
- Control Flow in Java: if, else and switch
- Loops in Java: for, while and do-while
- Methods
- Arrays and Strings
-
OOP
- Classes and Objects in Java
- Constructors in Java
- The this Keyword in Java
- The static Keyword in Java
- Encapsulation in Java
- Access Modifiers in Java
- Inheritance in Java
- Method Overriding and super in Java
- Polymorphism in Java
- Abstraction, Abstract Classes and Interfaces in Java
- Composition, Aggregation and Association in Java
- The Object Lifecycle in Java
- Core Java
- Exception Handling
-
Collections
- The Java Collections Framework
- List in Java: ArrayList, LinkedList, Vector and Stack
- Set in Java: HashSet, LinkedHashSet and TreeSet
- Map in Java: HashMap, LinkedHashMap and TreeMap
- How HashMap Works Internally in Java
- Queue and Deque in Java: ArrayDeque and PriorityQueue
- Iterators in Java
- Comparable and Comparator in Java
- Collections Utilities and Choosing the Right Collection
- Generics
- Java 8+
- Stream API
- Date and Time
- File and I/O
-
Multithreading
- Threads in Java: Processes, Runnable and Thread
- Thread Lifecycle in Java
- Synchronization in Java: synchronized and volatile
- Locks and Atomic Classes in Java
- Race Conditions and Deadlocks in Java
- The Executor Framework and Thread Pools in Java
- Future and CompletableFuture in Java
- Concurrent Collections in Java
- The Java Memory Model
- JVM and Memory
- Advanced Java
- Networking
- JDBC
- Testing
Queue
A Queue holds elements for processing. The usual discipline is first in, first out, but PriorityQueue deliberately breaks that and serves the smallest element first.
Two families of method
| Operation | Throws on failure | Returns a special value |
|---|---|---|
| Insert | add(e) | offer(e) returns false |
| Remove | remove() | poll() returns null |
| Examine | element() | peek() returns null |
Preferoffer,pollandpeek. Returningnullfor an empty queue is easier to handle than catchingNoSuchElementException, and it makes bounded queues behave sensibly.
Queue<String> jobs = new ArrayDeque<>();
jobs.offer("resize-image");
jobs.offer("send-email");
jobs.offer("build-index");
while (!jobs.isEmpty()) {
System.out.println("Running " + jobs.poll());
}
// resize-image, send-email, build-indexDeque
A double ended queue supports insertion and removal at both ends, so it can act as a queue, as a stack, or as both.
| Purpose | Head methods | Tail methods |
|---|---|---|
| Insert | offerFirst, addFirst, push | offerLast, addLast |
| Remove | pollFirst, removeFirst, pop | pollLast, removeLast |
| Examine | peekFirst | peekLast |
As a stack
Deque<String> history = new ArrayDeque<>();
history.push("page-1");
history.push("page-2");
history.push("page-3");
System.out.println(history.pop()); // page-3, last in first out
System.out.println(history.peek()); // page-2This is the modern replacement for java.util.Stack. It is faster, is not synchronised, and does not expose list operations that would let a caller insert into the middle.
As a queue
Deque<String> queue = new ArrayDeque<>();
queue.offerLast("first");
queue.offerLast("second");
System.out.println(queue.pollFirst()); // firstArrayDeque
Backed by a circular array. Both ends are O(1), there is no per element node object, and it is friendly to the processor cache.
- It does not allow
null, deliberately, becausenullis the "empty" signal frompollandpeek. - It is not thread safe. Use
ConcurrentLinkedDequeorLinkedBlockingDequewhen shared. - It is preferred over
LinkedListfor every queue and stack use.
PriorityQueue
Queue<Integer> smallestFirst = new PriorityQueue<>();
smallestFirst.addAll(List.of(50, 10, 40, 20));
System.out.println(smallestFirst.poll()); // 10
System.out.println(smallestFirst.poll()); // 20record Task(String name, int priority) { }
Queue<Task> tasks = new PriorityQueue<>(Comparator.comparingInt(Task::priority).reversed());
tasks.offer(new Task("backup", 1));
tasks.offer(new Task("alert", 9));
tasks.offer(new Task("report", 5));
System.out.println(tasks.poll()); // alert, highest priority firstIt is a binary heap, so offer and poll are O(log n) and peek is O(1).
Only the head is ordered. Printing aPriorityQueueor iterating it shows the heap array, not sorted order. The order is guaranteed only through repeatedpollcalls, which surprises people constantly.
System.out.println(smallestFirst); // e.g. [10, 20, 40, 50] - not a promiseChoosing a queue
| Need | Use |
|---|---|
| FIFO queue or stack, single thread | ArrayDeque |
| Serve by priority | PriorityQueue |
| Producer and consumer threads | LinkedBlockingQueue, ArrayBlockingQueue |
| Non blocking concurrent queue | ConcurrentLinkedQueue |
| Delayed or scheduled items | DelayQueue, PriorityBlockingQueue |
A worked example: balanced brackets
public static boolean isBalanced(String text) {
Deque<Character> open = new ArrayDeque<>();
String openers = "([{";
String closers = ")]}";
for (char c : text.toCharArray()) {
if (openers.indexOf(c) >= 0) {
open.push(c);
} else {
int index = closers.indexOf(c);
if (index >= 0) {
if (open.isEmpty() || open.pop() != openers.charAt(index)) {
return false;
}
}
}
}
return open.isEmpty();
}Common mistakes
- Adding
nullto anArrayDeque, which throws. - Expecting a
PriorityQueueto iterate in sorted order. - Using
java.util.Stackin new code. - Using
LinkedListas a queue whenArrayDequeis faster in every respect. - Mixing the throwing and the returning method families and being surprised by an exception.
- Sharing a plain
ArrayDequebetween threads.
Best practices
- Declare the variable as
QueueorDeque, and instantiateArrayDeque. - Use
offer,pollandpeek. - Give a
PriorityQueuean explicitComparatorunless natural order is obviously right. - Use a blocking queue from
java.util.concurrentfor producer and consumer designs. - Drain with
while ((item = queue.poll()) != null)rather than checkingisEmptyand then removing.
Practice
- Implement an undo history with a
Dequeand explain whyStackis a poorer choice. - Print a
PriorityQueuedirectly and then drain it, and explain the difference. - Why does
ArrayDequeforbidnullelements? - Build a task scheduler that always runs the highest priority task first.
- Rewrite a bracket matcher using
ArrayDequeand handle unmatched closers.
Conclusion
Use ArrayDeque for queues and stacks, PriorityQueue when order of service matters, and a blocking queue when threads hand work to each other. Prefer the methods that return a value over the ones that throw.