Java • Core Data Structures

Queue Interface — Use Queue interface for FIFO patterns (offer/poll/peek)

Master the first-in, first-out data structure that keeps your programs running in the right order.

Java programming emblem with a coffee cup, code braces, and terminal
Inside this lesson 10 sections 5 questions 6 figures

SECTION 1

Historical Context & Motivation

Long before computers existed, people understood the idea of a queue — a line where the first person to arrive is the first person served. Whether you are waiting at a coffee shop or standing in line at a theme park, you expect fair, ordered processing. Computer scientists formalized this everyday concept into a data structure that guarantees the same fairness: First-In, First-Out (FIFO).

In early computing, operating systems needed a way to schedule tasks so that no process was starved of CPU time. The queue became the go-to structure for task schedulers, print spoolers, and message-passing systems. When Java introduced its Collections Framework, it gave developers a clean, standardized Queue interface so everyone could rely on the same set of methods.

1950s • FIFO Concept in OS Design

Early operating systems adopted FIFO scheduling to manage jobs sent to batch-processing mainframes, ensuring tasks ran in the order they were submitted.

1998 • Java Collections Framework (JDK 1.2)

Java introduced its Collections Framework with List, Set, and Map interfaces. However, a dedicated Queue interface was not yet included.

2004 • Queue Interface Added (Java 5)

Java 5 added the java.util.Queue interface along with implementations like LinkedList and PriorityQueue, giving developers a standard FIFO API.

2004+ • Concurrent Queues

The java.util.concurrent package introduced thread-safe queues like ConcurrentLinkedQueue and ArrayBlockingQueue for multi-threaded applications.

The central question the Queue interface answers is: how can we process elements in the exact order they arrive, using a reliable and consistent API? Understanding this interface unlocks patterns used in everything from print queues to web server request handling.

SECTION 2

Core Principles & Definitions

The Queue interface in Java extends Collection and defines a contract for FIFO data structures. It provides two categories of methods: ones that throw exceptions when an operation fails, and "safe" methods that return special values instead. For everyday use, the safe methods — offer, poll, and peek — are the ones you will use most often.

1

FIFO Ordering

Elements leave the queue in the same order they entered. The first item added is the first item removed — just like a real-world line.
2

offer() — Enqueue

Adds an element to the tail (back) of the queue. Returns true on success, false if the queue is full.
3

poll() — Dequeue

Removes and returns the element at the head (front) of the queue. Returns null if the queue is empty.
4

peek() — Inspect

Returns the head element without removing it. Returns null if the queue is empty. Useful for checking the next item to process.
5

Interface ≠ Implementation

Queue is an interface. Common implementations include LinkedList and ArrayDeque. You program to the interface so you can swap implementations easily.
✦

KEY TAKEAWAY

Think of a queue like the drive-through lane at a fast-food restaurant. Cars enter at the back (offer), the car at the front gets served and leaves (poll), and you can look at who is next without making them leave (peek). Nobody cuts in line — that is FIFO.

SECTION 3

Visual Explanation

The diagram below shows how elements flow through a queue over a sequence of operations. Notice that elements always enter from the right (tail) and exit from the left (head). This is the hallmark of FIFO behavior.

Queue FIFO Operations — Step by StepStep 1: offer("A"), offer("B"), offer("C")ABCHEADTAILStep 2: poll() → returns "A"ABCHEADTAILStep 3: offer("D")BCDTAILHEADStep 4: peek() → returns "B" (no removal)BCD👁 peek → "B"Summary of Queue Stateoffer → adds to TAIL | poll → removes from HEAD | peek → reads HEAD without removingElements always travel from TAIL → HEAD (right to left in this diagram)
Step-by-step FIFO operations: elements enter at the tail via offer(), exit at the head via poll(), and can be inspected at the head via peek().

In the diagram above, you can see that after adding A, B, and C (Step 1), calling poll() removes A because it was added first (Step 2). When we add D via offer(), it goes to the back of the line (Step 3). Finally, peek() lets us see that B is next — without actually taking it out (Step 4). This is FIFO in action.

SECTION 4

How the Queue Interface Works in Java

The Queue<E> interface lives in java.util and extends Collection<E>. Because it is an interface, you cannot instantiate it directly. Instead, you create an object of a class that implements the interface. The two most common implementations for general FIFO use are LinkedList and ArrayDeque.

Two Families of Methods

The Queue interface deliberately provides two methods for each operation — one that throws an exception on failure and one that returns a special value. This design lets you choose the error-handling style that fits your situation.

OperationThrows ExceptionReturns Special Value
Insert at tailadd(e) — throws IllegalStateExceptionoffer(e) — returns false
Remove from headremove() — throws NoSuchElementExceptionpoll() — returns null
Examine headelement() — throws NoSuchElementExceptionpeek() — returns null

Queue method pairs: exception-throwing vs. special-value

💡

Best Practice

For most everyday code, prefer offer, poll, and peek. They let you handle edge cases with a simple null check or boolean test, avoiding the overhead and noise of try-catch blocks.

Declaring a Queue

You declare a variable using the interface type and assign it an implementation. This is called programming to the interface. For example: Queue<String> line = new LinkedList<>(); or Queue<Integer> tasks = new ArrayDeque<>();. The left side tells you what it does (Queue behavior), while the right side tells you how it does it (LinkedList or ArrayDeque implementation).

SECTION 5

Common Queue Implementations

Java provides several classes that implement the Queue interface. Choosing the right one depends on your use case. The diagram below illustrates the class hierarchy, and the table that follows compares the most common options.

Queue Interface — Class Hierarchy«interface»Collection<E>«interface»Queue<E>LinkedList<E>ArrayDeque<E>PriorityQueue<E>Doubly-linked nodesAlso implements List, DequeResizable arrayFastest general FIFO queueHeap-based orderingNOT strict FIFOQuick Rule of ThumbNeed standard FIFO? → ArrayDeque (or LinkedList)Need priority ordering? → PriorityQueue (not FIFO — smallest element comes out first)
The Queue interface sits between Collection and its concrete implementations. LinkedList and ArrayDeque provide true FIFO behavior, while PriorityQueue orders elements by priority instead.
ImplementationBacked ByFIFO?Null Elements?
LinkedListDoubly-linked listYesAllowed (but discouraged)
ArrayDequeResizable arrayYesNot allowed
PriorityQueueBinary heapNo — ordered by priorityNot allowed

Comparison of common Queue implementations

⚠

Why Not Use ArrayList?

An ArrayList does not implement Queue. While you could manually remove from index 0, that operation is O(n) because every other element must shift down. ArrayDeque and LinkedList both offer O(1) enqueue and dequeue, making them far more efficient.

SECTION 6

Worked Example — Print Job Queue

Imagine a school printer that receives print jobs from multiple students. Each job should be processed in the order it arrives. Let's write a small Java program that uses a Queue to model this situation.

Simulating a Print Queue

  1. 1

    Step 1 — Import and Declare the Queue

    We import java.util.Queue and java.util.LinkedList. Then we declare: Queue<String> printQueue = new LinkedList<>();. The variable type is Queue<String>, programming to the interface.
    Queue<String> printQueue = new LinkedList<>();
  2. 2

    Step 2 — Add Jobs with offer()

    Three students send print jobs. We enqueue each one using offer(): printQueue.offer("Essay - Alice"); printQueue.offer("Lab Report - Bob"); printQueue.offer("Slides - Carol");. Each call returns true because a LinkedList-backed queue is unbounded.
    Queue contents: [Essay - Alice, Lab Report - Bob, Slides - Carol]
  3. 3

    Step 3 — Check the Next Job with peek()

    Before printing, the system checks which job is next: String next = printQueue.peek();. This returns "Essay - Alice" without removing it from the queue. The size remains 3.
    peek() → "Essay - Alice" (queue unchanged)
  4. 4

    Step 4 — Process Jobs with poll()

    Now the printer processes jobs one at a time. Each call to poll() removes and returns the head element. First call: printQueue.poll() → "Essay - Alice". Second call: "Lab Report - Bob". Third call: "Slides - Carol".
    Jobs processed in FIFO order: Alice → Bob → Carol
  5. 5

    Step 5 — Handle the Empty Queue

    When the queue is empty and we call poll() again, it returns null instead of throwing an exception. Similarly, peek() returns null. This safe behavior is why these methods are preferred over remove() and element().
    poll() → null — no crash, no exception
🔁

PATTERN TO REMEMBER

A common loop for draining a queue is: while (!queue.isEmpty()) { process(queue.poll()); }. This loop will process every element in FIFO order and stop cleanly when the queue is empty.

SECTION 7

Queue vs. Stack vs. List

Queues are not the only way to store and retrieve elements in Java. It is important to understand how a Queue compares to a Stack (Last-In, First-Out) and a general-purpose List. Picking the right structure saves you from bugs and performance headaches.

FeatureQueue (FIFO)Stack (LIFO)List (Index-based)
Ordering ruleFirst in, first outLast in, first outAccess by index position
Insertoffer(e) — tailpush(e) — topadd(e) — end (or any index)
Removepoll() — headpop() — topremove(i) — any index
Inspectpeek() — headpeek() — topget(i) — any index
Typical use caseTask scheduling, BFSUndo history, DFSRandom access, ordered collections

Queue vs. Stack vs. List — key differences

✦

WHEN TO USE A QUEUE

Use a Queue whenever the order of processing matters and you want the oldest item handled first. If you need the newest item first, use a Stack (or Deque as a stack). If you need random access to any position, use a List. Choosing the right data structure communicates your intent and prevents other developers from using the collection incorrectly.

SECTION 8

Connection to Advanced Topics

The basic Queue interface is your starting point, but Java offers more powerful variations for specialized scenarios. As you advance, you will encounter these extended interfaces and patterns.

Basic QueueAdvanced VariantWhen You Need It
Queue<E>Deque<E>Insert and remove from both ends (double-ended queue). Can act as both a queue and a stack.
Queue<E>BlockingQueue<E>Thread-safe queue that blocks when you try to poll from an empty queue or offer to a full queue. Used in producer-consumer patterns.
Queue<E>PriorityQueue<E>Elements are ordered by natural ordering or a custom Comparator, not by insertion order. Useful for scheduling by urgency.

From basic Queue to advanced variants

One of the most important algorithms that relies on a queue is Breadth-First Search (BFS). In BFS, you explore a graph level by level, and a FIFO queue ensures you visit all neighbors of the current node before moving deeper. If you continue into AP Computer Science or college-level data structures, mastering Queue-based BFS will be essential for solving shortest-path problems, web crawlers, and social network analysis.

ℹ

Looking Ahead

Once you are comfortable with Queue, explore Deque (pronounced "deck"). ArrayDeque implements both Queue and Deque, so it can serve as a FIFO queue, a LIFO stack, or both at the same time — an incredibly versatile tool.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain in your own words why poll() is generally preferred over remove() when working with a Queue. What happens differently when the queue is empty?
Show answer
When a queue is empty, poll() returns null, allowing your program to handle the situation gracefully with a simple null check. In contrast, remove() throws a NoSuchElementException, which can crash your program if you do not wrap the call in a try-catch block. Using poll() leads to cleaner, more readable code and avoids the overhead of exception handling for a situation that is often a normal part of program flow (an empty queue).

Question 2: BASIC CALCULATION

Consider the following code. What does it print? Queue<Integer> q = new LinkedList<>(); q.offer(10); q.offer(20); q.offer(30); System.out.println(q.peek()); System.out.println(q.poll()); System.out.println(q.peek()); System.out.println(q.size());
Show answer
The output is: 10, 10, 20, 2. The first peek() returns 10 (the head) without removing it. Then poll() removes and returns 10, making 20 the new head. The second peek() returns 20. Finally, size() returns 2 because only 20 and 30 remain in the queue.

Question 3: INTERMEDIATE

Write a Java method void reverseFirstK(Queue<Integer> queue, int k) that reverses the order of the first k elements of a queue while leaving the remaining elements in the same relative order. You may use a Stack as a helper. Assume k ≤ queue.size().
Show answer
The strategy is: (1) Poll the first k elements from the queue and push them onto a stack. (2) Pop all k elements from the stack and offer them back into the queue (this reverses their order). (3) Poll and re-offer the remaining (size − k) elements so they move to the back of the queue after the reversed portion. For example, if the queue is [1, 2, 3, 4, 5] and k = 3, after the method it becomes [3, 2, 1, 4, 5]. The stack naturally reverses the order of the first k elements because it is LIFO.

Question 4: APPLIED

A customer-service call center receives calls and places them in a Queue. Write a complete Java program that: (1) creates a Queue<String> of caller names, (2) enqueues five callers, (3) processes them one at a time using a loop, printing "Now serving: [name]" for each, and (4) after all callers are processed, attempts one more poll() and prints whether the queue is empty.
Show answer
The program would import java.util.Queue and java.util.LinkedList, declare Queue<String> callQueue = new LinkedList<>(), then call offer() five times with different names. A while loop checks !callQueue.isEmpty() and inside the loop calls poll() to get and remove the next caller. After the loop, calling poll() returns null, confirming the queue is empty. This pattern — looping with poll until empty — is the standard way to drain a FIFO queue.

Question 5: CRITICAL THINKING

A student argues: "Since LinkedList implements both List and Queue, I should always declare my variable as LinkedList<String> data = new LinkedList<>(); so I have access to all methods from both interfaces." Explain why this reasoning is flawed and propose a better approach.
Show answer
Declaring the variable as LinkedList exposes all methods from both List and Queue, which actually makes the code worse. If your intent is FIFO processing, declaring Queue<String> data = new LinkedList<>() restricts the visible API to only Queue methods. This communicates your intent to other developers, prevents accidental misuse (like calling get(index) which would break the FIFO contract), and makes it easy to swap implementations later (for example, switching to ArrayDeque). This principle is called 'programming to the interface' and it leads to more maintainable, less error-prone code. Only widen the type when you truly need methods from multiple interfaces.

SUMMARY

Lesson Summary

Summary

The Java Queue interface provides a standardized API for FIFO (First-In, First-Out) data structures. Its three essential methods are offer() to add an element at the tail, poll() to remove and return the head element, and peek() to inspect the head without removing it. These methods return special values (false or null) on failure, making them safer than their exception-throwing counterparts add, remove, and element.

Common implementations include LinkedList and ArrayDeque, both offering O(1) enqueue and dequeue. Always declare your variable as Queue<E> rather than the concrete class to follow the principle of programming to the interface. As you advance, the Queue concept extends into Deque, BlockingQueue, and algorithms like Breadth-First Search — all built on the same FIFO foundation you learned here.

Varsity Tutors • Java • Queue Interface — Use Queue interface for FIFO patterns (offer/poll/peek)