The Java Queue Interface: A Hands-on Guide

Roger Winter



Picture a popular fast-food chain that also offers a “drive-thru” option. Customers line up in their cars to order, proceed to a pick-up point to receive their orders, and then leave. New customers join from the rear and exit in the order they arrive.

two people creating a java queue on a laptop

In Java, the Queue data structure works in a similar way — it follows the first-in-first-out (FIFO) principle. The Java Queue interface enables Java applications to hold ordered objects (or elements) while providing additional operations, including insertion, deletion, and inspection — all of which adhere to the FIFO rule.

The Queue interface also has robust implementing classes, including PriorityQueue, LinkedList, DelayQueue, ArrayBlockingQueue, and PriorityBlockingQueue.

In this guide, we’ll explore the most important aspects of the Queue interface, its core methods, detailed examples of queue operations, and thread-safety handling using BlockingQueues. Let’s dive in.

Download Now: An Introduction to Java & JavaScript

How to Use the Java Queue Interface

The Queue interface is a member of the java.util.* package, which extends the Collection interface — the root interface of the Java Collections framework.

Since this interface doesn’t have a direct implementation, subinterfaces like the List, Set, and Queue provide its implementation. Consequently, the Queue contains its own methods and those of the Collection interface.

Since the Queue is an interface, it can’t be instantiated. Therefore, it’s implemented by three main classes: the LinkedList, PriorityQueue, and ArrayDeque.

an illustration of the three classes in the java queue interface

Queue Interface: Common Methods

You can define the Queue interface and its signature methods as follows, where E is the element type contained in the queue:

public interface Queue<E> extends Collection<E> { E element(); boolean offer (E e); E peek(); E poll(); E remove(); boolean add (E e); E offer(); }

Here are the key operations that the methods above perform on queues:

  • element returns the queue’s head (leading) element. If the queue is empty, this method throws an exception.
  • offer (E e) inserts a new element into the queue and returns a boolean value depending on the outcome of the operation. The insertion is successful only if the operation does not violate any queue capacity restrictions.
  • peek also returns the head element of the queue. While similar to element, the difference between the two is that peek returns null, while the former throws an exception if the queue is empty.
  • poll: a dual-operation method that simultaneously retrieves and removes the queue’s head element. If the queue is empty, this method returns null.
  • remove: also removes and returns the head element of a queue but throws an exception if the data structure is empty
  • add (E e): a common method that adds an element into the queue only if the operation does not violate the structure’s capacity limit. The method returns true if the insertion is successful and throws an IllegalStateException if the queue has no space to accommodate a new element.  

Queue Interface Implementation

Next, let’s cover two classes to implement the queue interface, with examples: LinkedList and PriorityQueue.

LinkedList Example

The Java LinkedList class belongs to the collections framework and utilizes the doubly linked list principle to store items in the data structure. Instead of storing elements in contiguous memory locations, a LinkedList store uses nodes to hold data fields. Each node references the next one on the list. A node is characterized by three fields:

  • Prev stores the previous element’s address within the data structure and is always null for the first element.
  • Next holds the address to the next element in the list. For the last element in the list, this field is null.
  • Data the data held by the node.

an illustration of a node in the java queue interface

The example below create a queue of the LinkedList type to hold even numbers between 0 and 10. The LinkedList implements the methods explained earlier to demonstrate common operations on the data structure.

//Import Queue interface and LinkedList class import java.util.Queue; import java.util.LinkedList; public class LinkedListExample { public static void main (String[]args){ //Initialize queue to LinkedList Queue<Integer> evenIntegers = new LinkedList<Integer>(); //create the LinkedList int i; for (i = 0; i<=10; i+=2){ //Add operation evenIntegers.add(i); } //Print the LinkedList size System.out.println("The size of the LinkedList is: " + evenIntegers.size()); //Print the LinkedList System.out.println("All the elements in the LinkedList are: " + evenIntegers); //Print the head element System.out.println("The element at the top of the LinkedList is: " + evenIntegers.peek()); //Offer three new elements evenIntegers.offer(12); evenIntegers.offer(14); evenIntegers.offer(16); //Print the updated LinkedList System.out.println("New Updated List: " + evenIntegers); //Remove the head elements System.out.println("Remove element "+ evenIntegers.poll() + " from top of the list"); System.out.println("Remove element "+ evenIntegers.remove() + " from top of the list"); //Print the final LinkedList System.out.println("The Final LinkedList: " + evenIntegers); } } The size of the LinkedList is: 6 All the elements in the LinkedList are: [0, 2, 4, 6, 8, 10] The element at the top of the LinkedList is: 0 New Updated List: [0, 2, 4, 6, 8, 10, 12, 14, 16] Remove element 0 from top of the list Remove element 2 from top of the list The Final LinkedList: [4, 6, 8, 10, 12, 14, 16]

A common use case for LinkedList for developers is designing website navigation. The structure allows the linking of web resources together, enabling forward and backward navigation across browser windows.

PriorityQueue Example

Recall that a regular queue is a first-in-first-out (FIFO) structure — otems are processed in the order they come in. However, there are cases when some processes within a queue have a higher priority than others.

Suppose that a group of individuals arrives for the annual charity marathon in your local town square and queue by their order of arrival (first-in). Before starting the race, the participants must check in by confirming their registration against a manual register with the race organizers.

However, the manual register lists participants in ascending order of their first names. Processing participants may require re-ordering the queue to match the naming criteria, resulting in a priority queue.

While the elements entering a priority queue are not necessarily sorted, they are always retrieved in sorted order. The PriorityQueue class contains a comparator method that specifies the ordering comparator. If none is provided, the queue applies natural ordering — alphabetical or numerical — on its elements.

The example below uses a priority queue to sort fruits added to a queue. The priority is ascending order of the fruits’ names.

import java.util.ArrayList; import java.util.List; //Import PriorityQueue Class import java.util.PriorityQueue; import java.util.Queue; public class PriorityQueueExample { public static void main(String[]args){ //Initialize queue to a PriorityQueue Queue <String> fruitPriority = new PriorityQueue<String>(); //Offer fruits into the queue fruitPriority.offer("Mango"); fruitPriority.offer("Orange"); fruitPriority.offer("Peach"); fruitPriority.offer("Apple"); fruitPriority.offer("Banana"); fruitPriority.offer("Guava"); //Check the fruit at the top System.out.println("Fruit at the top of the priority queue: " + fruitPriority.peek()); //A list to store the sorted fruits List<String> sortedFruits = new ArrayList<String>(); //Insert the fruits into the list while (!fruitPriority.isEmpty()){ sortedFruits.add(fruitPriority.remove()); } //Print the list of sorted fruits System.out.println("The sorted fruits by priority: " + sortedFruits); } } Fruit at the top of the priority queue: Apple The sorted fruits by priority: [Apple, Banana, Guava, Mango, Orange, Peach]

Note that in the example above, the PriorityQueue doesn’t have a comparator argument. Therefore, the queue orders the “fruit” elements alphabetically.

You can apply priority queues in operating system processes to trigger high-priority processes, including the previsional-inactive ones, typically stored at the bottom of the queue.

Queues and Thread Safety

A common issue affecting LinkedList and PriorityQueue implementations is that they’re not thread-safe. While the shareability of Queues makes them great for multi-threaded processes, multiple threads attempting to perform read operations on a single queue may cause resource contention and reduced write speeds.

The BlockingQueue interface mitigates this problem by providing methods that force threads to defer operations based on the queue’s state. By implementing a blocking queue, write operations wait until the queue has available space while read operations wait for a non-empty queue before attempting element retrieval or removal.

Since BlockingQueue is an interface, it requires classes to implement it. ArrayBlockingQueue and LinkedBlockingQueue are classes that implement the interface.

an illustration of the ArrayBlockingQueue and LinkedBlockingQueue classes in the java queue interface

The BlockingQueue interface contains both the typical Queue implementation methods and two additional ones for added operations: put and take.

The put method supports multi-threaded processes by waiting until there is space in the array blocking queue before performing an element insertion. The take method waits until there are elements in the data structure before deleting or returning items from it.

BlockingQueue Implementation Using ArrayBlockingQueue

In this example, a BlockingQueue is implemented by the ArrayBlockingQueue class. The class contains a producer thread, which inserts even-numbered elements into the queue, and a consumer thread that retrieves from the same structure with a specified time delay.

//Import ArrayBlockingQueue import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; public class ArrayBlockingQueueExample { public static void main(String[] args) throws InterruptedException { BlockingQueue<Integer> evenBlockingQueue = new ArrayBlockingQueue<>(6); //Producer thread new Thread(() -> { int num = 0; try { while (true) { //Increment poll element by 2 evenBlockingQueue.put(num+=2); System.out.println("Added : " + num + " to the blocking queue"); //suspend thread for 2 milliseconds Thread.sleep(TimeUnit.SECONDS.toMillis(2)); } } catch (InterruptedException e) { e.printStackTrace(); } }).start(); //Initiate consumer thread new Thread(() -> { try { while (true) { //remove element from queue int pollQueue = evenBlockingQueue.take(); System.out.println("Retrieved : " + pollQueue + " from the blocking queue"); Thread.sleep(TimeUnit.SECONDS.toMillis(3)); } } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } } Added : 2 to the blocking queue Retrieved : 2 from the blocking queue Added : 4 to the blocking queue Retrieved : 4 from the blocking queue Added : 6 to the blocking queue Added : 8 to the blocking queue Retrieved : 6 from the blocking queue Added : 10 to the blocking queue Retrieved : 8 from the blocking queue Added : 12 to the blocking queue Retrieved : 10 from the blocking queue Added : 14 to the blocking queue

The program’s output demonstrates that the producer thread begins by adding an even number into the blocking queue, and the consumer thread only retrieves an element if it exists in the queue. The producer thread then resumes the insertion process because the array blocking queue has the space to accommodate new elements.

If you’re building highly concurrent systems, then BlockingQueue implementations are great for ensuring thread safety in producer-consumer processes such as event-streaming applications.

Implementing the Java Queue Interface

Using the Java Queue Interface in your applications enables the inclusion of robust data handling and processing structures that support element ordering, by default, and efficient methods for data manipulation, through implementing classes such as the LinkedList.

Even in cases where thread-safety is critical, the Queue interface supports safe multithreading while enforcing memory efficiency, making it ideal for many business cases that demand optimized data structures and related processes.

New Call-to-action

Topics: Java

Related Articles

We're committed to your privacy. HubSpot uses the information you provide to us to contact you about our relevant content, products, and services. You may unsubscribe from these communications at any time. For more information, check out our Privacy Policy.

Learn more about one of the world's most popular programming languages.