Data Structures and Algorithms

Priority Queue in Java

A PriorityQueue is a queue data structure that orders its elements based on their priority, implemented using a priority heap—a complete binary tree where each parent node adheres to a specific ordering rule. By default, it operates as a min-heap, meaning the smallest element (determined by natural ordering or a custom Comparator) is always at the front. However, it can be customized to function as a max-heap if needed.

Key Characteristics

  • Dynamic Size: Automatically adjusts as elements are added or removed.
  • Ordering: Elements follow natural ordering (e.g., ascending for integers) unless a custom Comparator is specified.
  • No Null Elements: Null insertions are not allowed, throwing a NullPointerException.
  • Unbounded: Grows as needed, constrained only by system memory.
  • Not Thread-Safe: Use PriorityBlockingQueue for multithreaded environments.

These traits make PriorityQueue a versatile choice for priority-based processing.

Creating a PriorityQueue

Java offers multiple constructors to instantiate a PriorityQueue, providing flexibility based on your requirements:

  1. Default Constructor
    Creates a queue with an initial capacity of 11 and natural ordering.
				
					PriorityQueue<Integer> pq = new PriorityQueue<>();

				
			

2. With Initial Capacity
Specifies a starting capacity while retaining natural ordering.

				
					PriorityQueue<Integer> pq = new PriorityQueue<>(20);

				
			

3. With Custom Comparator
Defines a custom ordering rule.

				
					PriorityQueue<Integer> pq = new PriorityQueue<>(Comparator.reverseOrder()); // Max-heap

				
			

From a Collection
Initializes the queue with elements from another collection.

				
					List<Integer> list = Arrays.asList(3, 1, 2);
PriorityQueue<Integer> pq = new PriorityQueue<>(list);

				
			

Choosing the right constructor depends on your use case, such as needing a specific order or pre-populating the queue.

Basic Operations on PriorityQueue

Here are the fundamental methods to interact with a PriorityQueue:

Adding Elements
Use add(E e) or offer(E e) to insert elements (both work similarly in this context).
java
Copy

				
					pq.add(5);
pq.offer(3);

				
			

Peeking at the Head
The peek() method retrieves the head (smallest element) without removing it.

				
					System.out.println("Head: " + pq.peek()); // Output: 3

				
			

Removing the Head
The poll() method retrieves and removes the head.

				
					int head = pq.poll(); // Returns and removes 3

				
			

Checking Size
Use size() to determine the number of elements.

				
					int size = pq.size();

				
			

Checking if Empty
The isEmpty() method verifies if the queue is empty.

				
					boolean empty = pq.isEmpty();

				
			

ese operations are the building blocks for working with PriorityQueue.

Practical Example: Task Management

Let’s see PriorityQueue in action with a simple task management scenario, where lower numbers indicate higher priority.

				
					import java.util.PriorityQueue;

public class TaskManager {
    public static void main(String[] args) {
        PriorityQueue<Integer> taskQueue = new PriorityQueue<>();

        // Adding tasks with priority levels
        taskQueue.add(3);
        taskQueue.add(1);
        taskQueue.add(2);
        taskQueue.add(5);
        taskQueue.add(4);

        // Processing tasks by priority
        while (!taskQueue.isEmpty()) {
            System.out.println("Processing task with priority: " + taskQueue.poll());
        }
    }
}

				
			

 Output:

				
					Processing task with priority: 1
Processing task with priority: 2
Processing task with priority: 3
Processing task with priority: 4
Processing task with priority: 5

				
			

This demonstrates the min-heap behavior, where tasks are processed from lowest to highest priority.

Custom Ordering with Comparator

For scenarios requiring a different ordering (e.g., max-heap or custom objects), a Comparator can be used. Here’s an example with a Task class:

				
					import java.util.PriorityQueue;
import java.util.Comparator;

class Task {
    int priority;
    String description;

    Task(int priority, String description) {
        this.priority = priority;
        this.description = description;
    }

    @Override
    public String toString() {
        return description + " (Priority: " + priority + ")";
    }
}

public class CustomPriorityQueue {
    public static void main(String[] args) {
        Comparator<Task> priorityComparator = Comparator.comparingInt(t -> t.priority);
        PriorityQueue<Task> taskQueue = new PriorityQueue<>(priorityComparator);

        taskQueue.add(new Task(3, "Medium priority task"));
        taskQueue.add(new Task(1, "High priority task"));
        taskQueue.add(new Task(2, "Low priority task"));

        while (!taskQueue.isEmpty()) {
            System.out.println("Processing: " + taskQueue.poll());
        }
    }
}

				
			


Output:

				
					Processing: High priority task (Priority: 1)
Processing: Low priority task (Priority: 2)
Processing: Medium priority task (Priority: 3)

				
			

 This flexibility makes PriorityQueue adaptable to diverse needs.

How PriorityQueue Works Internally

Internally, PriorityQueue relies on a binary heap:

  • Min-Heap by Default: The smallest element is at the root.
  • Adding Elements: New elements are added at the end and “bubble up” to their correct position (O(log n)).
  • Removing Elements: The root is removed, the last element moves to the root, and it “bubbles down” (O(log n)).

This structure ensures efficient priority-based operations, with a time complexity of O(log n) for add and poll.

Important Notes

  • No Nulls: Adding null throws a NullPointerException.
  • Comparable Requirement: Elements must implement Comparable or use a Comparator, or a ClassCastException occurs.
  • Not Synchronized: Use PriorityBlockingQueue for thread safety.
  • Iterator Order: The iterator() doesn’t guarantee priority order; use poll() instead.
  • Capacity: Grows dynamically from an initial capacity (default 11).

Applications of PriorityQueue

PriorityQueue shines in various real-world scenarios:

  • Task Scheduling: Prioritize tasks by urgency.
  • Graph Algorithms: Used in Dijkstra’s and Prim’s algorithms.
  • Event Simulations: Process events in chronological order.
  • Huffman Coding: Build trees for data compression.

Conclusion

The PriorityQueue in Java is a robust, heap-based data structure for managing prioritized elements efficiently. Whether you’re handling basic operations or customizing it with a Comparator, it offers both simplicity and power. By understanding its mechanics and limitations, such as lack of thread safety, you can integrate it seamlessly into your projects. Experiment with PriorityQueue to unlock its full potential in your Java programming journey!

Can I use PriorityQueue with custom objects?

Yes, if the objects implement Comparable or you provide a Comparator.

No specific order is guaranteed for equal priorities.

No, you must remove and re-add it with the updated priority.

add and poll are O(log n), while peek is O(1).

add and poll are O(log n), while peek is O(1).

DSA, High & Low Level System Designs

Buy for 60% OFF
₹25,000.00 ₹9,999.00

Accelerate your Path to a Product based Career

Boost your career or get hired at top product-based companies by joining our expertly crafted courses. Gain practical skills and real-world knowledge to help you succeed.

Reach Out Now

If you have any queries, please fill out this form. We will surely reach out to you.

Contact Email

Reach us at the following email address.

arun@getsdeready.com

Phone Number

You can reach us by phone as well.

+91-97737 28034

Our Location

Rohini, Sector-3, Delhi-110085

WhatsApp Icon

Master Your Interviews with Our Free Roadmap!

Hi Instagram Fam!
Get a FREE Cheat Sheet on System Design.

Hi LinkedIn Fam!
Get a FREE Cheat Sheet on System Design

Loved Our YouTube Videos? Get a FREE Cheat Sheet on System Design.