Queues
First in, first out
A queue is the opposite restriction from a stack. You add at the back and remove from the front, so items come out in the order they went in.
The everyday picture is a line of people waiting: you join at the back, and the person at the front is served next.
| Operation | Meaning | When empty |
|---|---|---|
offer / add | Add an item at the back | — |
poll | Remove and return the front item | Returns null |
remove | Remove and return the front item | Throws |
peek | Look at the front item | Returns null |
Java gives you two names for most operations. The offer/poll/peek family returns a signal value when something goes wrong; the add/remove/element family throws. Pick one style and stay with it — mixing them is how you end up with an unexpected exception.
Using one
ArrayDeque again, this time declared as a Queue.
import java.util.ArrayDeque;
import java.util.Queue;
Queue<String> tasks = new ArrayDeque<>();
tasks.offer("calibrate");
tasks.offer("driveForward");
tasks.offer("shoot");
System.out.println(tasks.peek()); // calibrate
System.out.println(tasks.poll()); // calibrate
System.out.println(tasks.poll()); // driveForward
System.out.println(tasks.size()); // 1
Draining a queue completely:
while (!tasks.isEmpty()) {
String task = tasks.poll();
System.out.println("running: " + task);
}
Do not use LinkedList as your queue by default
LinkedList also implements Queue, and older code often uses it. ArrayDeque is faster and uses less memory, for the cache reasons described in Linked Lists.
The exception is if you genuinely need null elements, which ArrayDeque rejects. That is rare and usually a sign the design should change instead.
Where queues show up
Task scheduling. Actions queued in the order they should run, then drained one per cycle.
class ActionScheduler {
private final Queue<String> pending = new ArrayDeque<>();
void schedule(String action) {
pending.offer(action);
}
void runNext() {
String action = pending.poll();
if (action != null) {
System.out.println("executing " + action);
}
}
boolean hasWork() { return !pending.isEmpty(); }
}
Buffering readings. Collect sensor values as they arrive and process them in order.
Breadth-first search. This is the big one. Exploring a grid or graph outward from a starting point, level by level, is a queue at its core — and it is what finds shortest paths in an unweighted graph. See Graphs.
Queue<int[]> frontier = new ArrayDeque<>();
frontier.offer(new int[]{startRow, startCol});
while (!frontier.isEmpty()) {
int[] cell = frontier.poll();
// look at each neighbour and offer the unvisited ones
}
Swapping that queue for a stack turns breadth-first search into depth-first search. The structure choice is the algorithm choice, which is a good illustration of why these restrictions matter.
Priority queues
Sometimes arrival order is the wrong order. A PriorityQueue always hands back the smallest element, whatever order things were added.
import java.util.PriorityQueue;
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.offer(50);
pq.offer(10);
pq.offer(30);
System.out.println(pq.poll()); // 10
System.out.println(pq.poll()); // 30
System.out.println(pq.poll()); // 50
For your own types, supply a comparator saying what “smallest” means:
class Task {
String name;
int priority;
Task(String name, int priority) { this.name = name; this.priority = priority; }
}
PriorityQueue<Task> queue =
new PriorityQueue<>((a, b) -> Integer.compare(a.priority, b.priority));
queue.offer(new Task("shoot", 3));
queue.offer(new Task("stopMotors", 1));
queue.offer(new Task("driveForward", 2));
System.out.println(queue.poll().name); // stopMotors
Use Integer.compare(a, b) rather than a - b. Subtraction can overflow for large values and give the wrong sign — a bug that only appears on extreme inputs.
For largest-first, reverse the comparison:
new PriorityQueue<>((a, b) -> Integer.compare(b.priority, a.priority));
A PriorityQueue is not sorted
This trips people up regularly:
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.offer(50); pq.offer(10); pq.offer(30);
System.out.println(pq); // may print [10, 50, 30]
for (int v : pq) System.out.print(v); // NOT in sorted orderOnly peek and poll respect priority. Printing it or iterating over it gives the internal heap layout, which is not sorted. To get every element in order, poll until empty.
Costs
| Structure | Add | Remove front | Peek |
|---|---|---|---|
ArrayDeque as a queue | Instant | Instant | Instant |
PriorityQueue | Proportional to log of size | Proportional to log of size | Instant |
Common mistakes
- Mixing the throwing and returning families, then being surprised by an exception.
- Not checking for
nullafterpollon a possibly-empty queue. - Expecting a
PriorityQueueto iterate in order. - Subtraction in a comparator, which can overflow.
- Using
LinkedListwhereArrayDequeis faster. - Offering
nullto anArrayDeque, which throws.
Practice
- Offer the numbers 1 through 5 to a queue, then poll and print them. Confirm the order is preserved, unlike a stack.
- Write a class that holds the last 10 sensor readings, discarding the oldest when an eleventh arrives.
- Write a method that takes a
Queue<String>of task names and runs them in order, printing each. - Use a
PriorityQueueto print five tasks in priority order, lowest number first. - Given a grid of
.and#, use a queue to count how many.cells are reachable from the top-left.
Hints
- Straight application; contrast with the stack exercise.
- Offer each new reading; if
size()exceeds 10,pollone off the front. - Drain with a
while (!queue.isEmpty())loop. - A comparator on the priority field.
- Breadth-first search. Offer the start cell, then repeatedly poll and offer the unvisited open neighbours. Mark cells visited when you offer them, not when you poll — otherwise the same cell gets queued several times.