Queue and Deque are related Java collection interfaces for holding elements before processing. The difference is how many ends of the collection your code needs to use.
Quick answer
Use Queue when the workflow is simple first-in, first-out processing. Use Deque when you need efficient operations at both the front and the back.
For most single-threaded queue or stack-like workflows, ArrayDeque is a strong default. Use PriorityQueue when priority matters, and use concurrent queue implementations when multiple threads share the structure.
What Queue means
Queue models waiting-line behavior. You add elements to the back and remove from the front.
Queue<String> jobs = new ArrayDeque<>();
jobs.offer("send-email");
jobs.offer("generate-report");
String next = jobs.poll(); // send-email
The common methods are:
| Method | Meaning |
|---|---|
offer(e) | Add an element if possible. |
poll() | Remove and return the head, or return null if empty. |
peek() | Read the head without removing it, or return null if empty. |
add(e) | Add, but may throw if capacity is restricted. |
remove() | Remove, but throws if empty. |
element() | Peek, but throws if empty. |
In most application code, offer, poll, and peek are easier to handle because they avoid exceptions for normal empty-queue cases.
What Deque means
Deque means double-ended queue. It supports insertion and removal at both ends.
Deque<String> deque = new ArrayDeque<>();
deque.addLast("page-1");
deque.addLast("page-2");
deque.addFirst("priority-page");
System.out.println(deque.removeFirst()); // priority-page
This is useful when the workflow sometimes treats the front differently from the back: work stealing, undo stacks, breadth-first search, sliding windows, parsers, and recent-item buffers.
Deque can also act as a stack:
Deque<String> stack = new ArrayDeque<>();
stack.push("open");
stack.push("validate");
String step = stack.pop(); // validate
Prefer ArrayDeque over the legacy Stack class for most stack behavior.
ArrayDeque as the default
ArrayDeque is usually the best default implementation for non-concurrent queue and deque workflows.
It is backed by a resizable array, supports fast operations at both ends, and avoids the per-node overhead of LinkedList.
Deque<Long> recentIds = new ArrayDeque<>();
recentIds.addLast(101L);
recentIds.addLast(102L);
if (recentIds.size() > 100) {
recentIds.removeFirst();
}
This pattern is useful for bounded recent-history buffers, local processing queues, and algorithms that need front/back movement.
Queue vs PriorityQueue
Queue does not always mean FIFO. PriorityQueue implements Queue, but it removes elements by priority, not insertion order.
Queue<Integer> numbers = new PriorityQueue<>();
numbers.offer(30);
numbers.offer(10);
numbers.offer(20);
System.out.println(numbers.poll()); // 10
If the next item should be chosen by score, deadline, cost, or priority, use PriorityQueue. If all items have equal priority and should be processed by arrival order, use ArrayDeque.
Read Java PriorityQueue Explained for heap behavior and ordering pitfalls.
LinkedList as a queue
LinkedList implements both List and Deque, so it can be used as a queue.
Queue<String> queue = new LinkedList<>();
That works, but it is rarely the best default. Each LinkedList element is a node object, which adds memory overhead and pointer chasing. For most local queue workflows, ArrayDeque is simpler and faster.
Read ArrayList vs LinkedList in Java for more detail on node overhead.
Concurrency choices
ArrayDeque is not thread-safe. If multiple threads need to add and remove items, choose a concurrent collection or a queue from java.util.concurrent.
Common options include:
ConcurrentLinkedQueuefor non-blocking FIFO behavior.LinkedBlockingQueuefor blocking producer/consumer workflows.ArrayBlockingQueuefor bounded blocking queues.PriorityBlockingQueuefor concurrent priority-based processing.
The collection choice should match the ownership model. If one method or one request owns the queue, ArrayDeque is usually enough. If workers share it, use a concurrent queue and design shutdown, backpressure, and error handling.
Practical selection notes
For most backend code, choose the interface that communicates the intended access pattern. If callers should only add at the back and remove from the front, expose Queue. If callers need both ends, expose Deque. Avoid exposing LinkedList or ArrayDeque directly unless the concrete type is part of the contract.
ArrayDeque is often a strong default for in-memory queues and stacks because it avoids per-node allocation overhead. LinkedList can still appear in older examples, but it is rarely the first choice for new queue-like code unless list-node behavior is specifically needed.
Common mistakes
The first mistake is using LinkedList as the default queue because it implements Queue. Prefer ArrayDeque unless you have a clear reason not to.
The second mistake is using Queue when the code needs both ends. If operations use both front and back, declare the variable as Deque so the type communicates the workflow.
The third mistake is assuming every Queue is FIFO. PriorityQueue is a queue interface implementation with priority-based removal.
The fourth mistake is sharing ArrayDeque across threads without synchronization.
Related reading
Read Java PriorityQueue Explained for priority-based queues and ArrayList vs LinkedList in Java for why LinkedList is not usually the best default queue. Read Time Complexity of Java Collections for queue and deque complexity notes. Use the Big-O Cheat Sheet when comparing collection operations. For the full sequence, browse the Java Collections Learning Path and the Topics map.