What is Queue and what implementations exist?
4. ConcurrentLinkedQueue — lock-free 5. BlockingQueue — Producer-Consumer 6. null forbidden in most queues 7. SynchronousQueue — for direct handoff
🟢 Junior Level
Queue — data structure following FIFO principle (First In, First Out).
Simple analogy: Queue at a store. First come, first served.
Main methods:
| Action | Exception | Safe method |
|---|---|---|
| Add | add(e) |
offer(e) |
| Retrieve | remove() |
poll() |
| Peek | element() |
peek() |
Queue<String> queue = new ArrayDeque<>();
queue.offer("A"); // Added
queue.offer("B");
String first = queue.poll(); // → "A" (first left)
String next = queue.peek(); // → "B" (peeked)
offer/poll/peek preferred in production code because they don’t throw exceptions — return false/null on error, allowing graceful handling.
🟡 Middle Level
Main implementations
ArrayDeque:
// Circular array → O(1) insertion/removal
Queue<String> queue = new ArrayDeque<>();
queue.offer("A"); // Fast
queue.poll(); // Fast
PriorityQueue:
// Binary Heap → extract by priority
Queue<Integer> pq = new PriorityQueue<>();
pq.offer(5);
pq.offer(1);
pq.offer(3);
pq.poll(); // → 1 (smallest!)
Methods: Exception vs Special Value
// Throw exception:
queue.add(e); // IllegalStateException if full
queue.remove(); // NoSuchElementException if empty
queue.element(); // NoSuchElementException if empty
// Return special value:
queue.offer(e); // false if full
queue.poll(); // null if empty
queue.peek(); // null if empty
// ✅ In production use offer/poll/peek!
Thread-safe queues
// Lock-free (high throughput)
Queue<String> q = new ConcurrentLinkedQueue<>();
// Blocking (Producer-Consumer)
BlockingQueue<String> q = new LinkedBlockingQueue<>();
q.put("A"); // Blocks if full
String e = q.take(); // Blocks if empty
// Bounded (memory control)
BlockingQueue<String> q = new ArrayBlockingQueue<>(100);
// → Max 100 elements → OOM protection
🔴 Senior Level
When NOT to use Queue
- If random access needed — use List
- If uniqueness needed — use Set
- If data needs sorting — PriorityQueue with comparator or TreeSet
Why queues don’t like null?
// Most queues forbid null
queue.offer(null); // → NullPointerException!
// Why?
// poll() and peek() return null for empty queue
// If null is valid element → ambiguity!
// → "queue empty" or "null at head"?
BlockingQueue patterns
// Producer-Consumer
BlockingQueue<Task> queue = new LinkedBlockingQueue<>(1000);
// Producer
queue.put(task); // Blocks if full → backpressure!
**Backpressure** — mechanism where slow consumer signals fast producer to slow down. Bounded Queue blocks producer until consumer frees space.
// Consumer
Task task = queue.take(); // Blocks if empty
SynchronousQueue
// Zero capacity queue!
BlockingQueue<String> q = new SynchronousQueue<>();
// put blocks until someone does take
// Block = thread "sleeps" and doesn't consume CPU until another thread takes element. Effective for synchronization without busy-wait.
q.put("A"); // Waiting...
// In another thread:
String s = q.take(); // → "A", put unblocks
// Usage: Executors.newCachedThreadPool()
DelayQueue
// Elements available only after delay
class DelayedTask implements Delayed {
private final long deadline;
public long getDelay(TimeUnit unit) {
return unit.convert(deadline - System.currentTimeMillis(), MILLISECONDS);
}
}
DelayQueue<DelayedTask> queue = new DelayQueue<>();
// poll() returns element only when getDelay() <= 0
TransferQueue (Java 7+)
// Producer waits until Consumer takes element
TransferQueue<String> q = new LinkedTransferQueue<>();
// Producer
q.transfer("message"); // Blocks until consumer takes!
// Consumer
String msg = q.take(); // Takes, producer unblocks
// Usage: synchronous data transfer
// Synchronous transfer = producer waits for exact moment when consumer *accepts* element (not just puts in buffer). Analogy: handing file person-to-person vs leaving in mailbox.
Production Experience
Real case: Bounded Queue saved from OOM
- Producer faster than Consumer → queue growing
- Unbounded Queue → OOM in 2 hours
- Solution: ArrayBlockingQueue(10000)
- Result: Producer blocks → stable system
Best Practices
- offer/poll/peek instead of add/remove/element
- ArrayDeque — for single-thread
- Bounded Queue — for backpressure
- ConcurrentLinkedQueue — lock-free
- BlockingQueue — Producer-Consumer
- null forbidden in most queues
- SynchronousQueue — for direct handoff
Summary for Senior
- Queue = FIFO, offer/poll/peek preferred
- ArrayDeque — fastest single-thread
- BlockingQueue — Producer-Consumer with backpressure
- Bounded — OOM protection
- ConcurrentLinkedQueue — lock-free
- TransferQueue — synchronous handoff
- null forbidden (ambiguity with poll)
🎯 Interview Cheat Sheet
Must know:
- Queue = FIFO (First In, First Out), methods: offer/poll/peek preferred over add/remove/element
- offer/poll/peek return false/null instead of exception — safer for production
- ArrayDeque — fastest single-thread (circular array, O(1) insertion/removal)
- PriorityQueue = Binary Heap, extract by priority (smallest element), O(log n)
- BlockingQueue = Producer-Consumer pattern, put/take block, bounded queue = backpressure
- SynchronousQueue = zero capacity, producer blocks until consumer takes element
- TransferQueue (Java 7+) — synchronous handoff: transfer() blocks until consumer takes
- null forbidden in most queues — ambiguity with poll() returning null
Common follow-up questions:
- Why offer/poll/peek better than add/remove/element? — Don’t throw exceptions, allow graceful handling
- Why bounded queue? — Backpressure: slow consumer blocks fast producer, OOM protection
- How SynchronousQueue differs from regular queue? — Zero capacity, put blocks until someone does take
- When to use TransferQueue? — For synchronous data transfer, producer waits for exact acceptance moment
Red flags (NOT to say):
- ❌ “Queue allows storing null” — most implementations forbid null (ambiguity with poll)
- ❌ “Unbounded Queue — decent choice for production” — without bounded queue risk OOM from fast producer
- ❌ “add/remove/element — best choice” — they throw exceptions, offer/poll/peek safer
- ❌ “PriorityQueue extracts largest element” — by default extracts SMALLEST (need reverse Comparator for largest)
Related topics:
- [[What are the main Collection Framework interfaces]]
- [[What is Deque]]
- [[What is the difference between List, Set and Queue]]
- [[What is Stack]]