Queue Interface Java - Feature ImageQueue Interface Java - Feature Image
HappyCoders Glasses

Queue Interface in Java

Sven Woltmann
Sven Woltmann
April 26, 2022

Since Java 5.0, the JDK contains the interface java.util.Queue and several queue implementations, which differ in various properties (bounded/unbounded, blocking/non-blocking, thread-safe/non-thread-safe).

I will discuss all of these characteristics in the remaining part of this tutorial.

Java Queue Class Hierarchy

Before I present the Java queue in detail, I would like to give an overview in the form of a UML class diagram:

Java Queue class hierarchy
Java Queue class hierarchy

I will describe the BlockingQueue interface in the next part of the tutorial.

The concrete queue classes ConcurrentLinkedQueue, PriorityQueue, ArrayBlockingQueue, DelayQueue, LinkedBlockingQueue, PriorityBlockingQueue, and SynchronousQueue follow. Finally, I will explain the TransferQueue interface together with the LinkedTransferQueue.

You can jump to the corresponding parts at any time using the tutorial navigation on the right margin.

The grayed-out interfaces Deque and BlockingDeque and their implementations are covered in the tutorial series on deques.

Java Queue Methods

The Queue interface defines six methods for inserting, removing, and viewing elements. For each of the three queue operations "Enqueue", "Dequeue", and "Peek", the interface defines two methods: one that throws an exception in case of an error and one that returns a special value (false or null).

Methods for Inserting into the Queue

First, a graphical overview of the enqueue methods:

Methods for insertion into a queue: add(), offer()
Methods for insertion into a queue

Queue.add()

This method is already defined in the Collection interface and inserts an element into the queue. On success, the method returns true. If a bounded (size-restricted) queue is full, this method throws an IllegalStateException.

Queue.offer()

offer(), like add(), adds an element to the queue and returns true on success. If a bounded queue is full, this method returns false instead of throwing an IllegalStateException.

Methods for Removing from the Queue

Also for the dequeue methods, first a graphical overview:

Methods for removing from a queue: remove(), poll()
Methods for removing from a queue

Queue.remove()

remove() removes the element from the queue's head. If the queue is empty, the method throws a NoSuchElementException.

Queue.poll()

poll(), too, removes the element at the head of the queue. Unlike remove(), the method does not throw an exception if the queue is empty but returns null.

Methods for Viewing the Head Element

And again, first an overview of methods:

Methods for viewing the queue's head element: element(), peek()
Methods for viewing the queue's head element

Queue.element()

The element() method returns the element from the head of the queue without removing it from the queue. If the queue is empty, a NoSuchElementException is thrown.

Queue.peek()

Like element(), peek() also returns the head element without removing it from the queue. However, if the queue is empty, this method returns null, just like poll().

Queue Methods – Summary

The following table shows the six methods again grouped by operation and type of error handling:

In case of error:
exception
In case of error:
return value
Adding an element (enqueue):add(E e)offer(E e)
Removing an element (dequeue):remove()poll()
Viewing an element (peek):element()peek()

How to Create a Queue?

java.util.Queue is an interface. An interface cannot be instantiated because it only describes what methods a class offers but does not contain implementations of those methods.

What happens if you still try?

public class QueueTest { public static void main(String[] args) { Queue<Integer> queue = new Queue<>(); // <-- Don't do this! } }
Code language: Java (java)

When trying to compile this code, you would see the following error message:

QueueTest.java:5: error: Queue is abstract; cannot be instantiated Queue<Integer> queue = new Queue<>(); // <-- Don't do this! ^ 1 error
Code language: plaintext (plaintext)

Therefore, you must select one of the concrete queue implementations, e.g., ConcurrentLinkedQueue:

Queue<Integer> queue = new ConcurrentLinkedQueue<>();
Code language: Java (java)

(I will explain the different queue classes in later parts of this tutorial. In the last part, you will find a decision guide on when to use which implementation.)

Example: How to Use a Queue?

The following example shows how to create a queue, fill it with some values, and retrieve the values. You can also find the example code on GitHub.

public class JavaQueueDemo { public static void main(String[] args) { // 1. Queue<Integer> queue = new ConcurrentLinkedQueue<>(); // 2. for (int i = 1; i <= 5; i++) { queue.offer(i); System.out.println("queue.offer(" + i + ") --> queue = " + queue); } System.out.println(); // 3. System.out.println("queue.peek() = " + queue.peek()); System.out.println(); // 4. while (!queue.isEmpty()) { System.out.println("queue.poll() = " + queue.poll() + " --> queue = " + queue); } System.out.println(); // 5. System.out.println("queue.poll() = " + queue.poll()); System.out.println("queue.peek() = " + queue.peek()); } }
Code language: Java (java)

The program does the following (the numbering refers to the comments in the source code):

  1. It creates a queue. Which one you use is irrelevant for this example since it doesn't require any special queue properties. We will use ConcurrentLinkedQueue.
  2. Using Queue.offer(), we write the values 1 to 5 to the queue. And we display the queue's content after each insertion.
  3. We look at the queue's head element using Queue.peek().
  4. As long as the queue contains elements (we check this with the isEmpty() method, which the Queue interface inherits from Collection), we retrieve these elements with Queue.poll() and display them. After that, we show the entire content of the queue again.
  5. After the queue has been emptied, we once again display the return values of poll() and peek().

The program prints the following:

queue.offer(1) --> queue = [1] queue.offer(2) --> queue = [1, 2] queue.offer(3) --> queue = [1, 2, 3] queue.offer(4) --> queue = [1, 2, 3, 4] queue.offer(5) --> queue = [1, 2, 3, 4, 5] queue.peek() = 1 queue.poll() = 1 --> queue = [2, 3, 4, 5] queue.poll() = 2 --> queue = [3, 4, 5] queue.poll() = 3 --> queue = [4, 5] queue.poll() = 4 --> queue = [5] queue.poll() = 5 --> queue = [] queue.poll() = null queue.peek() = null
Code language: plaintext (plaintext)

You can see very nicely how the elements are taken out in the same order as they were inserted (First-in-first-out – FIFO).

Summary and Outlook

In this part of the tutorial, you have learned about Java's Queue interface. Using an example, you have seen how to use the queue.

In the next part, we will look at the BlockingQueue interface. I will also explain the difference between bounded and unbounded or blocking and non-blocking queues.

After that, we will look at all of the JDK's queue implementations individually. Based on their unique characteristics, I will explain when to use which implementation.

If you still have questions, please ask them via the comment function. Do you want to be informed about new tutorials and articles? Then click here to sign up for the HappyCoders.eu newsletter.