A Queue is a fundamental data structure that follows the FIFO (First-In-First-Out) principle. It allows elements to be inserted at the end (rear) and removed from the front. In this guide, we will walk through the implementation of a Queue in Java using a linked list approach. We’ll cover how to create a Queue class and implement core operations such as enqueue, dequeue, peek, isEmpty, size, and demonstrate its usage with sample operations.
Understanding the Queue Structure
Node Class
First, we define a Node
class to represent each element in the queue. Each node contains a data element and a reference to the next node in the queue.
class Node<T> {
T data;
Node<T> next;
Node(T data) {
this.data = data;
this.next = null;
}
}
Queue Class
Next, we implement the Queue
class using the linked list structure provided by the Node
class.
import java.util.NoSuchElementException;
public class Queue<T> {
private Node<T> front; // Front of the queue
private Node<T> rear; // Rear of the queue
private int size; // Size of the queue
public Queue() {
front = null;
rear = null;
size = 0;
}
// Method to add an element to the rear of the queue
public void enqueue(T data) {
Node<T> newNode = new Node<>(data);
if (isEmpty()) {
front = newNode; // If queue is empty, new node is both front and rear
} else {
rear.next = newNode; // Link the current rear to the new node
}
rear = newNode; // Update the rear to be the new node
size++;
}
// Method to remove and return the element from the front of the queue
public T dequeue() {
if (isEmpty()) {
throw new NoSuchElementException("Queue is empty. Cannot dequeue.");
}
T data = front.data; // Retrieve data at the front
front = front.next; // Move front pointer to the next node
if (front == null) {
rear = null; // If front becomes null, queue is empty
}
size--;
return data;
}
// Method to return the element at the front of the queue without removing it
public T peek() {
if (isEmpty()) {
throw new NoSuchElementException("Queue is empty. Cannot peek.");
}
return front.data; // Return data at the front
}
// Method to check if the queue is empty
public boolean isEmpty() {
return size == 0;
}
// Method to return the size of the queue
public int size() {
return size;
}
// Method to convert the queue to a string representation
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Node<T> current = front;
while (current != null) {
sb.append(current.data).append(" ");
current = current.next;
}
return sb.toString();
}
// Main method for testing the queue
public static void main(String[] args) {
Queue<Integer> queue = new Queue<>();
// Enqueue elements
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
System.out.println("Queue after enqueues: " + queue);
// Dequeue elements
System.out.println("Dequeued: " + queue.dequeue());
System.out.println("Queue after dequeue: " + queue);
// Peek at the front element
System.out.println("Front element: " + queue.peek());
// Check if the queue is empty
System.out.println("Is queue empty? " + queue.isEmpty());
// Get the size of the queue
System.out.println("Queue size: " + queue.size());
// Dequeue remaining elements
System.out.println("Dequeued: " + queue.dequeue());
System.out.println("Dequeued: " + queue.dequeue());
System.out.println("Is queue empty? " + queue.isEmpty());
}
}
Explaining the Queue Implementation
Node Class
- Node: Represents a node in the linked list structure.
- data: Holds the actual data of type
T
. - next: Points to the next node in the queue.
Queue Class
- front: Points to the front of the queue.
- rear: Points to the rear of the queue.
- size: Tracks the number of elements in the queue.
Constructor
- Initializes an empty queue with
front
andrear
set tonull
, andsize
to0
.
Methods
- enqueue(T data):
- Adds an element to the rear of the queue.
- Updates
rear
to point to the new node. - If the queue is empty, updates
front
to the new node.
- dequeue():
- Removes and returns the element from the front of the queue.
- Updates
front
to point to the next node. - If
front
becomesnull
, updatesrear
tonull
indicating the queue is empty.
- peek():
- Returns the element at the front of the queue without removing it.
- Throws
NoSuchElementException
if the queue is empty.
- isEmpty():
- Checks if the queue is empty.
- Returns
true
ifsize
is0
, otherwisefalse
.
- size():
- Returns the current size of the queue.
- toString():
- Converts the queue to a string representation.
- Useful for debugging and displaying the contents of the queue.
Main Method
- Demonstrates various operations on the queue:
- Enqueueing elements.
- Dequeueing elements.
- Peeking at the front element.
- Checking if the queue is empty.
- Getting the size of the queue.
Running the Code
To run this code:
- Copy the code into a file named
Queue.java
. - Compile it using
javac Queue.java
. - Run it using
java Queue
.
Output
The program demonstrates the queue operations and prints:
Queue after enqueues: 10 20 30
Dequeued: 10
Queue after dequeue: 20 30
Front element: 20
Is queue empty? false
Queue size: 2
Dequeued: 20
Dequeued: 30
Is queue empty? true
Conclusion
Implementing a queue from scratch helps in understanding the fundamental concepts of data structures and algorithms. This custom queue class provides all basic operations required for FIFO processing of elements. Whether you’re preparing for interviews or working on projects, mastering these foundational concepts is essential for efficient software development.
Feel free to modify the code, add more functionalities, or integrate it with other data structures. Experimenting with such implementations will enhance your understanding and problem-solving skills in programming.
I hope this guide helps you understand and implement a Queue in Java effectively! If you have any questions or suggestions, feel free to leave a comment below. Happy coding!