Topic 11: Collections in Java - Queue
📖 6 min read · 🎯 Advanced · 🧭 Prerequisites: collections-in-java-list, android-event-handling
Why this matters
Picture this: you open a food delivery app and place an order. Behind the scenes, dozens of orders are arriving every second — and the kitchen has to process them in the exact order they came in. No skipping the line, no jumping ahead. That's not just fairness — it's correctness. If your app processes things out of order, you get bugs that are really hard to explain. In Java, the Queue collection is built exactly for this — First In, First Out, or FIFO. Let's see how it works and when you'll reach for it in your Android code.
What You'll Learn
- Understand the Queue interface and the FIFO (First In, First Out) principle in Java
- Implement a
Queue<String>usingLinkedListas the backing data structure - Use
add(),poll(), andpeek()to manage queue entries - Integrate a Java queue into a working Android Activity with a live UI
The Analogy
Picture a customer support hotline on a busy Monday morning. The first caller who dials in is the first one an agent picks up — nobody cuts the line, nobody gets skipped, and the agent always clears the oldest call before taking a new one. The queue of callers forms at the moment of first contact and dissolves one-by-one as each person is helped. A Java Queue works exactly this way: elements enter at the tail and leave from the head, preserving arrival order with mathematical certainty. Jump-the-line logic simply doesn't exist in this data structure unless you reach for a PriorityQueue — and even then you're trading FIFO for a defined ordering rule, not chaos.
Chapter 1: The Queue Interface and FIFO
A Queue in Java is an interface in the java.util package that enforces FIFO — First In, First Out ordering. Elements are inserted at the tail and retrieved from the head. This makes queues the right tool whenever processing order must mirror arrival order: print spoolers, network packet buffers, task schedulers, and support-ticket systems all rely on this guarantee.
Key Queue methods you will use constantly:
| Method | Behavior on empty queue | Returns |
|---|---|---|
add(e) | throws IllegalStateException | boolean |
offer(e) | returns false | boolean |
poll() | returns null | head element or null |
remove() | throws NoSuchElementException | head element |
peek() | returns null | head element or null |
element() | throws NoSuchElementException | head element |
For Android work, prefer offer/poll/peek — they never throw on empty, which means no surprise crashes on the UI thread.
LinkedList is the most common Queue implementation in standard Java. It satisfies the Queue interface and handles unbounded growth gracefully, making it a natural fit for ticket-style scenarios where you don't know the maximum load in advance.
Chapter 2: Building the TicketManager
Create a new Android Studio project named TicketManager. The core logic lives in a dedicated TicketManager class that wraps the queue so the Activity doesn't touch the data structure directly.
import java.util.LinkedList;
import java.util.Queue;
public class TicketManager {
private Queue<String> ticketQueue;
public TicketManager() {
ticketQueue = new LinkedList<>();
}
// Adds a new support ticket to the tail of the queue
public void addTicket(String ticket) {
ticketQueue.add(ticket);
System.out.println("Ticket added: " + ticket);
}
// Removes and processes the head ticket; safe on empty queue
public void processNextTicket() {
String nextTicket = ticketQueue.poll();
if (nextTicket != null) {
System.out.println("Processing ticket: " + nextTicket);
} else {
System.out.println("No tickets to process.");
}
}
// Returns the head ticket without removing it; returns null if empty
public String peekNextTicket() {
return ticketQueue.peek();
}
}
Three things to note:
- Queue Initialization —
ticketQueue = new LinkedList<>()assigns aLinkedListinstance to theQueue<String>reference. The variable type is the interface; the runtime type is the implementation. This is standard Java polymorphism and lets you swap the backing structure later without changing the rest of the class. - Adding Tickets —
addTicketcallsticketQueue.add(ticket), which appends to the tail. - Processing Tickets —
processNextTicketcallsticketQueue.poll(), which atomically removes and returns the head element, or returnsnullif the queue is empty. - Peeking —
peekNextTicketcallsticketQueue.peek(), which reads the head element without consuming it. Useful for displaying "up next" in the UI without advancing state.
Chapter 3: Integrating with an Android Activity
Now wire the TicketManager into a real Android UI. The layout gives the user an input field, an add button, a process button, and a status label.
<!-- res/layout/activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:id="@+id/ticketInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter ticket"/>
<Button
android:id="@+id/addTicketButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add Ticket"/>
<Button
android:id="@+id/processTicketButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Process Next Ticket"/>
<TextView
android:id="@+id/ticketStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Ticket Status"/>
</LinearLayout>
// MainActivity.java
package com.example.ticketmanager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private TicketManager ticketManager;
private EditText ticketInput;
private TextView ticketStatus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ticketManager = new TicketManager();
ticketInput = findViewById(R.id.ticketInput);
ticketStatus = findViewById(R.id.ticketStatus);
Button addTicketButton = findViewById(R.id.addTicketButton);
addTicketButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String ticket = ticketInput.getText().toString();
if (!ticket.isEmpty()) {
ticketManager.addTicket(ticket);
ticketInput.setText("");
}
}
});
Button processTicketButton = findViewById(R.id.processTicketButton);
processTicketButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ticketManager.processNextTicket();
ticketStatus.setText("Next Ticket: " + ticketManager.peekNextTicket());
}
});
}
}
What each piece does:
addTicketButtonclick handler — reads theEditText, guards against empty input, callsaddTicket, then clears the field so the user can enter the next one.processTicketButtonclick handler — callsprocessNextTicket(which pops the head and logs it), then immediately callspeekNextTicketto show the new head in theticketStatuslabel. If the queue is now empty,peek()returnsnulland the status shows"Next Ticket: null"— you'd want to guard that in a production app.
sequenceDiagram
participant User
participant MainActivity
participant TicketManager
participant Queue as LinkedList<String>
User->>MainActivity: types "Bug #42", taps Add
MainActivity->>TicketManager: addTicket("Bug #42")
TicketManager->>Queue: add("Bug #42")
Queue-->>TicketManager: ok (tail updated)
User->>MainActivity: taps Process Next
MainActivity->>TicketManager: processNextTicket()
TicketManager->>Queue: poll()
Queue-->>TicketManager: "Bug #42" (head removed)
TicketManager-->>MainActivity: (logs "Processing ticket: Bug #42")
MainActivity->>TicketManager: peekNextTicket()
TicketManager->>Queue: peek()
Queue-->>TicketManager: next head or null
TicketManager-->>MainActivity: next ticket string
MainActivity->>User: updates ticketStatus TextView
🧪 Try It Yourself
Task: Extend TicketManager with a viewAllTickets() method that returns a formatted string listing every pending ticket in queue order (head first), then display this string in a new TextView when a "View All" button is tapped.
Success criterion: After adding tickets "Bug #1", "Bug #2", and "Bug #3" in that order, tapping "View All" should display:
Pending: Bug #1, Bug #2, Bug #3
Starter snippet:
public String viewAllTickets() {
if (ticketQueue.isEmpty()) {
return "No pending tickets.";
}
StringBuilder sb = new StringBuilder("Pending: ");
for (String ticket : ticketQueue) {
sb.append(ticket).append(", ");
}
// Remove trailing ", "
return sb.substring(0, sb.length() - 2);
}
Wire a new Button (@+id/viewAllButton) and TextView (@+id/allTicketsView) into the layout and call ticketManager.viewAllTickets() in its click handler.
🔍 Checkpoint Quiz
Q1. What does FIFO stand for, and which Queue method removes and returns the head element without throwing an exception on an empty queue?
Q2. Given the following code, what does output contain after execution?
Queue<String> q = new LinkedList<>();
q.add("alpha");
q.add("beta");
q.add("gamma");
q.poll();
String output = q.peek();
A) "alpha"
B) "beta"
C) "gamma"
D) null
Q3. The processTicketButton click handler calls processNextTicket() and then peekNextTicket(). Why does it call peek after poll rather than before?
A) peek only works after poll is called
B) To display the next ticket that will be processed, not the one just processed
C) poll resets the queue so peek returns the first element again
D) There is no functional difference; the order doesn't matter
Q4. You are building a notification delivery system. Notifications must be delivered in the order they were generated, and you need to handle the case where the queue is empty gracefully (no crashes). Which implementation best fits?
A) ArrayList with manual index tracking
B) Queue<Notification> backed by LinkedList, using offer() and poll()
C) Queue<Notification> backed by LinkedList, using add() and remove()
D) HashMap<Integer, Notification> keyed by arrival timestamp
A1. FIFO = First In, First Out. The method is poll() — it returns null on an empty queue instead of throwing NoSuchElementException like remove() does.
A2. B) "beta". poll() removes "alpha" (the head). After that the head is "beta", and peek() returns it without removing it.
A3. B) To display the next ticket that will be processed. After poll() removes the just-processed ticket, peek() looks at the new head — i.e., the ticket that will be processed on the next button tap. Calling peek before poll would show the ticket about to be processed right now, which is less useful as a status message.
A4. B) Queue<Notification> backed by LinkedList, using offer() and poll(). offer/poll are the null-safe variants that don't throw on capacity or empty conditions, which is critical for UI-thread safety in Android.
🪞 Recap
- A Java
Queueenforces FIFO order — the first element in is the first element out. LinkedListis the standardQueueimplementation for unbounded, order-sensitive workloads.add()/remove()/element()throw exceptions on failure;offer()/poll()/peek()returnnullorfalse— prefer the latter in Android code.poll()removes and returns the head;peek()reads the head without removing it.- Wrapping the queue in a dedicated manager class (like
TicketManager) keeps Activity code clean and the queue logic testable in isolation.
📚 Further Reading
- Java Queue Interface — Oracle Docs — the source of truth for every method contract
- LinkedList as a Queue — Baeldung — practical walkthrough of LinkedList queue behavior with examples
- Android UI Threading Best Practices — why poll/peek safety matters when queues touch the main thread
- ⬅️ Previous: Android Event Handling
- ➡️ Next: Collections in Java - Set