Java Circular Queue implementation (2024)
What is circular queue in Java?
Circular Queue is a linear data structure wherein operations are performed using the FIFO (First In First Out) method, and the last position is connected back to the first to form a circle.
Circular Queues, also known as Ring Buffers, were established to solve a queue's constraint of not being able to use the unoccupied spaces left at the beginning after the rear reaches its end spot.
When to use Circular Queues instead Regular Queues?
When working with a regular queue, elements can be added to it until it is full. When that limit is reached, no more additions can be made, even if there is a vacancy at the front of the queue. To fill that vacancy in a regular queue, we'd have to move all elements ahead to make room for an extra element at the back. This issue can be avoided by employing a Circular Queue into which FIFO data can be inserted until the queue reaches its maximum capacity. This is detailed further below.
Regular queue operations
Queue with single item
In this case, we have a regular queue with one item that serves as both the Front and Rear.Enqueue item to queue
Fill the queue with random items to make the most of its available space. The front and back items are indicated below in diagram.Dequeue item from queue
When we delete an item from the front end, the newly created vacancy is not easily accessible because new elements can only be added from the rear end.Enqueue elements to queue (shifting existing items forward)
To fill the front vacancy, we must first shift all items forward before adding the new element from the rear. Circular Queues come in handy in this situation.
Circular Queue Operations
-
Circular queue with only one element
The circular queue only has one element with the value 1. As a result, both the front and rear point to the same single element. -
Enqueue elements to the circular queue
We are presently enqueuing elements to fill the circular queue. As a result, the element with value 8 is our rear element, and the element with value 1 is our first element, as seen below: -
Dequeue an element from circular queue
Then, from the circular queue, dequeue one element with the value 1. As a result, our first element will now be element with value 2, as seen below: -
Add new element to circular queue
When a new element is added to the circular queue, there is no need to adjust the current elements; only the rear pointer must be moved to the appropriate spot, as seen below:
Java Circular Queue Program
package com.techgeeknext;
import java.util.Arrays;
public class CircularQueueExample {
public static void main(String[] args) {
CircularQueue<Integer> numCircularQueue = new CircularQueue(8);
numCircularQueue.enqueue(1);
numCircularQueue.enqueue(2);
numCircularQueue.enqueue(3);
numCircularQueue.enqueue(4);
numCircularQueue.enqueue(5);
numCircularQueue.enqueue(6);
numCircularQueue.enqueue(7);
numCircularQueue.enqueue(8);
System.out.println("Initial Circular Queue" + numCircularQueue);
System.out.print("Dequeued from circular queue at location ");
System.out.println(numCircularQueue.dequeue() + " ");
numCircularQueue.enqueue(9);
System.out.println("After enqueueing 9 to the circular queue: "+numCircularQueue);
}
}
/**
* Circular Queue implementation using Generics
*/
class CircularQueue<E> {
private int currentSize; //Current Circular Queue Size
private E[] circularQueueElements;
private int maxSize; //Circular Queue maximum size
private int rear;//rear position of Circular queue(new element enqueued at rear).
private int front; //front position of Circular queue(element will be dequeued from front).
public CircularQueue(int maxSize) {
this.maxSize = maxSize;
circularQueueElements = (E[]) new Object[this.maxSize];
currentSize = 0;
front = -1;
rear = -1;
}
/**
* Insert elements to rear.
*/
public void enqueue(E item) throws QueueFullException {
if (isFull()) {
throw new QueueFullException("Circular Queue is full. Element cannot be added");
} else {
rear = (rear + 1) % circularQueueElements.length;
circularQueueElements[rear] = item;
currentSize++;
if (front == -1) {
front = rear;
}
}
}
/**
* Remove element from Front.
*/
public E dequeue() throws QueueEmptyException {
E deQueuedElement;
if (isEmpty()) {
throw new QueueEmptyException("The Circular Queue is empty. The element could not be retrieved.");
} else {
deQueuedElement = circularQueueElements[front];
circularQueueElements[front] = null;
front = (front + 1) % circularQueueElements.length;
currentSize--;
}
return deQueuedElement;
}
/**
* Check if queue is full.
*/
public boolean isFull() {
return (currentSize == circularQueueElements.length);
}
/**
* Check if queue is empty.
*/
public boolean isEmpty() {
return (currentSize == 0);
}
@Override
public String toString() {
return "CircularQueue [" + Arrays.toString(circularQueueElements) + "]";
}
}
class QueueFullException extends RuntimeException {
private static final long serialVersionUID = 1L;
public QueueFullException() {
super();
}
public QueueFullException(String message) {
super(message);
}
}
class QueueEmptyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public QueueEmptyException() {
super();
}
public QueueEmptyException(String message) {
super(message);
}
}