Chapter 9: Circular Queue (Java)
Difficulty: Beginner → Intermediate
Prerequisites: Queue Fundamentals, Queue Using Array, Arrays, Java Basics
Learning Objectives
After completing this chapter, you will be able to:
- Understand why a Circular Queue is needed.
- Learn how a Circular Queue solves the memory wastage problem of a linear queue.
- Understand the wrap-around concept using the modulo (
%) operator. - Implement a Circular Queue from scratch in Java.
- Handle overflow and underflow conditions.
- Perform dry runs for all queue operations.
- Analyze time and space complexity.
- Answer common interview questions confidently.
Introduction
In the previous chapter, we implemented a Linear Queue using an Array.
Although enqueue and dequeue operations were O(1), the implementation had one major drawback:
The queue wastes memory after dequeue operations because freed positions at the beginning of the array cannot be reused.
A Circular Queue eliminates this problem by treating the array as if its first and last positions are connected in a circle.
Instead of stopping when rear reaches the last index, it wraps around to the beginning of the array if space is available.
Why Do We Need a Circular Queue?
Consider a queue of size 5.
Initially:
Index
0 1 2 3 4
10 20 30 _ _
Front = 0
Rear = 2Now perform:
dequeue()
dequeue()Queue becomes:
Index
0 1 2 3 4
10 20 30 _ _
↑
Front
Rear = 2Now insert:
enqueue(40)
enqueue(50)Memory:
Index
0 1 2 3 4
10 20 30 40 50
Front = 2
Rear = 4The queue contains only:
30 40 50However, trying:
enqueue(60)produces:
Queue OverflowAlthough indexes 0 and 1 are free.
How Circular Queue Solves This Problem
Instead of treating the array as a straight line, imagine it as a circle.
Index 0
●
● ●
Index 4 Index 1
● ●
Index 3 Index 2When the rear reaches the last index:
Rear = capacity - 1the next insertion goes back to:
Rear = 0This process is called wrap-around.
Circular Queue Visualization
Capacity = 5
Initially
0
+-------+
4 / \ 1
| |
3 \ / 2
+-------+Front = -1
Rear = -1After inserting:
10
20
30Index
0 1 2 3 4
10 20 30 _ _
Front = 0
Rear = 2After removing:
10
20
Index
0 1 2 3 4
10 20 30 _ _
Front = 2
Rear = 2Now insert:
40
50
Index
0 1 2 3 4
10 20 30 40 50
Front = 2
Rear = 4Next insertion:
enqueue(60)Instead of Overflow
Rear wraps around:
Index
0 1 2 3 4
60 20 30 40 50
Rear = 0
Front = 2Logical Queue
Front
↓
30 → 40 → 50 → 60
↑
RearNotice:
Even though memory looks unordered, the logical order remains correct.
The Modulo (%) Operator
The entire Circular Queue works because of one simple formula:
rear = (rear + 1) % capacity;Suppose:
capacity = 5| Current Rear | Calculation | New Rear |
| ------------ | ----------- | -------- |
| 0 | (0+1)%5 | 1 |
| 1 | (1+1)%5 | 2 |
| 2 | (2+1)%5 | 3 |
| 3 | (3+1)%5 | 4 |
| 4 | (4+1)%5 | 0 ✅ |This is called circular indexing.
Similarly,
front = (front + 1) % capacity;moves the front pointer correctly after a dequeue.
Circular Queue States
Empty Queue
Front = -1
Rear = -1Queue[]
Queue with One Element
Front
↓
10
↑
RearWrapped Queue
Index
0 1 2 3 4
60 _ 30 40 50
Front = 2
Rear = 0Logical Queue
30 → 40 → 50 → 60Queue Operations
| Operation | Description |
| --------- | ----------------------- |
| Enqueue | Insert at Rear |
| Dequeue | Remove from Front |
| Peek | View Front element |
| isEmpty | Check if Queue is empty |
| isFull | Check if Queue is full |
| Size | Number of elements |
| Display | Print Queue |Enqueue Operation
Current Queue
Front
↓
30 → 40 → 50
↑
RearInsert
enqueue(60)Algorithm
If Queue is Full
Overflow
Else If Queue is Empty
Front = Rear = 0
Else
Rear = (Rear + 1) % Capacity
Insert valueJava Implementation of Enqueue
public void enqueue(int value) {
if (isFull()) {
System.out.println("Queue Overflow");
return;
}
if (isEmpty()) {
front = rear = 0;
} else {
rear = (rear + 1) % capacity;
}
queue[rear] = value;
size++;
}Dry Run of Enqueue
Capacity = 5
Initially
Front = -1
Rear = -1enqueue(10)
Front = 0Rear = 010
enqueue(20)
Rear = 110 20enqueue(30)
Rear = 210 20 30enqueue(40)
Rear = 3enqueue(50)
Rear = 4dequeue()
Front becomes
1enqueue(60)
Get Pavan Pithdiya’s stories in your inbox
Join Medium for free to get updates from this writer.
Rear
(4+1)%5=0Queue becomes
60 20 30 40 50Logical order
20 30 40 50 60Dequeue Operation
Current Queue
20 30 40 50 60Remove
20Algorithm
If Queue Empty
Underflow
Else
value = queue[front]
front = (front + 1) % capacityJava Implementation of Dequeue
public int dequeue() {
if (isEmpty()) {
System.out.println("Queue Underflow");
return -1;
}
int value = queue[front];
if (front == rear) {
front = rear = -1;
} else {
front = (front + 1) % capacity;
}
size--;
return value;
}Peek Operation
public int peek() {
if (isEmpty()) {
System.out.println("Queue is Empty");
return -1;
}
return queue[front];
}isEmpty()
public boolean isEmpty() {
return front == -1;
}isFull()
A Circular Queue is full when moving rear one step ahead would make it equal to front.
public boolean isFull() {
return (rear + 1) % capacity == front;
}Example
Capacity = 5
Front = 2
Rear = 1
(1+1)%5 = 2
Front = 2
Queue FullDisplay Operation
public void display() {
if (isEmpty()) {
System.out.println("Queue is Empty");
return;
}
int i = front;
while (true) {
System.out.print(queue[i] + " ");
if (i == rear)
break;
i = (i + 1) % capacity;
}
System.out.println();
}Complete Java Implementation
public class CircularQueue {
private int[] queue;
private int front;
private int rear;
private int size;
private int capacity;
public CircularQueue(int capacity) {
this.capacity = capacity;
queue = new int[capacity];
front = -1;
rear = -1;
size = 0;
}
public boolean isEmpty() {
return front == -1;
}
public boolean isFull() {
return (rear + 1) % capacity == front;
}
public void enqueue(int value) {
if (isFull()) {
System.out.println("Queue Overflow");
return;
}
if (isEmpty()) {
front = rear = 0;
} else {
rear = (rear + 1) % capacity;
}
queue[rear] = value;
size++;
}
public int dequeue() {
if (isEmpty()) {
System.out.println("Queue Underflow");
return -1;
}
int value = queue[front];
if (front == rear) {
front = rear = -1;
} else {
front = (front + 1) % capacity;
}
size--;
return value;
}
public int peek() {
if (isEmpty()) {
System.out.println("Queue is Empty");
return -1;
}
return queue[front];
}
public int size() {
return size;
}
public void display() {
if (isEmpty()) {
System.out.println("Queue is Empty");
return;
}
int i = front;
while (true) {
System.out.print(queue[i] + " ");
if (i == rear)
break;
i = (i + 1) % capacity;
}
System.out.println();
}
public static void main(String[] args) {
CircularQueue queue = new CircularQueue(5);
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
queue.enqueue(40);
queue.enqueue(50);
queue.display();
System.out.println("Removed: " + queue.dequeue());
System.out.println("Removed: " + queue.dequeue());
queue.enqueue(60);
queue.enqueue(70);
queue.display();
System.out.println("Front: " + queue.peek());
System.out.println("Size: " + queue.size());
}
}Sample Output
10 20 30 40 50Removed: 10
Removed: 2030 40 50 60 70Front: 30
Size: 5
Dry Run
Initial
[]enqueue(10)
10enqueue(20)
10 20enqueue(30)
10 20 30dequeue()
Removed10Queue
20 30enqueue(40)
20 30 40enqueue(50)
20 30 40 50enqueue(60)
Rear wraps to index 0
Logical Queue
20 30 40 50 60Edge Cases
Queue Overflow
Queue
10 20 30 40 50Trying
enqueue(60)Output
Queue OverflowQueue Underflow
Queue
[]Calling
dequeue();Output
Queue UnderflowSingle Element Queue
Queue
10After
dequeue()Both pointers reset.
Front = -1
Rear = -1Time Complexity
| Operation | Time Complexity |
| --------- | --------------- |
| Enqueue | O(1) |
| Dequeue | O(1) |
| Peek | O(1) |
| isEmpty | O(1) |
| isFull | O(1) |
| Size | O(1) |
| Search | O(n) |
| Display | O(n) |Space Complexity
For n elements:
Space Complexity = O(n)Linear Queue vs Circular Queue
| Feature | Linear Queue | Circular Queue |
| ----------------------------------- | ------------ | -------------- |
| Memory Reuse | ❌ No | ✅ Yes |
| Overflow Before Array Is Truly Full | Yes | No |
| Wrap-around | No | Yes |
| Efficient Space Utilization | No | Yes |
| Enqueue | O(1) | O(1) |
| Dequeue | O(1) | O(1) |Advantages
- Eliminates memory wastage found in linear queues.
- Efficiently reuses freed array positions.
- All primary operations run in O(1) time.
- Ideal for fixed-size buffers and scheduling systems.
- Simple wrap-around logic using the modulo operator.
Disadvantages
- Fixed capacity unless resized manually.
- Wrap-around logic is more complex than a linear queue.
- Incorrect pointer updates can lead to subtle bugs.
Real-World Applications
- CPU scheduling (Round Robin)
- Circular buffers
- Keyboard input buffers
- Audio and video streaming
- Network packet buffering
- Embedded systems
- Producer–Consumer problems
Common Interview Questions
- Why is a Circular Queue better than a Linear Queue?
- What role does the modulo (
%) operator play? - How do you detect if a Circular Queue is full?
- How do you distinguish between an empty queue and a full queue?
- What happens when
rearreaches the last index? - Why are
enqueue()anddequeue()both O(1)? - Where are Circular Queues used in real-world systems?
Common Mistakes
- Forgetting to use the modulo operator while updating
frontorrear. - Incorrectly checking the full condition.
- Not resetting
frontandrearwhen removing the last element. - Confusing the physical array layout with the logical queue order.
Best Practices
- Maintain a separate
sizevariable when possible—it simplifies state management. - Use the modulo operator consistently for pointer updates.
- Reset the queue to the empty state (
front = rear = -1) after removing the last element. - For production Java code, prefer
ArrayDequeunless a fixed-capacity circular buffer is specifically required.
Chapter Summary
- A Circular Queue is an optimized array-based queue that treats the array as a circle.
- It solves the memory wastage problem of a linear queue by reusing freed positions through wrap-around.
- The modulo (
%) operator enables efficient circular indexing for bothfrontandrear. - Core operations (
enqueue,dequeue,peek) execute in O(1) time. - Circular Queues are widely used in operating systems, networking, embedded systems, and buffering applications where efficient use of fixed-size memory is essential.
Next Chapter
Chapter 10 — Queue Using Linked List (Java)
In the next chapter, you’ll learn:
- Dynamic Queue implementation using a Linked List.
- Node structure and memory layout.
- Why linked-list queues don’t suffer from fixed-capacity limitations.
- Complete Java implementation.
- Detailed dry runs.
- Complexity analysis.
- Comparison with array-based and circular queues.
- Interview questions and best practices.










