Course contents

PriorityQueue and binary heaps

PriorityQueue internals with binary min-heaps, offer and poll mechanics, comparators, iteration-order gotchas, update patterns and interview use cases.

PremiumMust know for interviewsLesson13 min readCoreReviewed Oct 2026 · Java 21
On this page
  1. Picture it first: a tournament bracket stored flat
  2. PriorityQueue under the hood
  3. Offer and poll, step by step
  4. Advanced: comparators, iteration, heapify and updates
  5. Edge cases you should say out loud
  6. How to say it in the interview

🧺 What is PriorityQueue?

PriorityQueue is a queue where the next item is chosen by priority, not by insertion time. In Java it is implemented as a binary heap in an array, so peek() gives you the smallest element by natural order, or the smallest according to the comparator you supplied. If you add tasks with priorities 5, 1 and 3, the task with priority 1 is at the head even if it was added last.

Think of it as a hospital triage desk, not a supermarket line. People still arrive one by one, but the next person called is the most urgent case according to the triage rule.

What you’ll learn

  • Explain how PriorityQueue stores a binary min-heap inside an Object[]
  • Trace offer, peek and poll through sift up and sift down
  • State the real costs: O(log n) offer and poll, O(1) peek, O(n) contains and remove(Object)
  • Avoid the classic gotcha that iteration order is not sorted
  • Build max-heaps and domain queues with safe Comparator chains, not subtraction
  • Handle ties, priority updates, nulls, capacity growth and thread-safety honestly

For this lesson, we'll focus on the concrete Java 21 PriorityQueue: the array-backed binary min-heap, index math, offer, poll, peek, comparator-based max-heaps, growth, nulls, stability, and the task-manager-by-priority scenario interviewers like. We will mention Dijkstra and merging K sorted lists because they are heap-shaped problems, but Top-K has its own lesson: Top-K with a heap.

Below the line (out of scope)

The 30-second answer

PriorityQueue is a priority-ordered queue backed by a binary min-heap in an Object[]. For an element at index i, the children are 2*i + 1 and 2*i + 2, and the parent is (i - 1) / 2. offer appends at the end and sifts up until the heap property is restored. poll removes the root, moves the last element to index 0, then sifts down. peek is O(1), offer and poll are O(log n), and contains or remove(Object) are O(n) because the heap is not searchable by value. It rejects null, is not thread-safe, iteration is not sorted, and equal priorities are not FIFO unless you add a sequence-number tie-breaker.

Interview radar

Asked very often: Which collection processes tasks by priority instead of arrival order? Senior rounds probe: Why heap construction from a collection is O(n), why updating a priority means remove plus re-add, and why iteration order is not sorted. Production angle: A retry scheduler where an overflow comparator or missing tie-breaker quietly runs jobs in the wrong order.

01Picture it first: a tournament bracket stored flat

A binary heap looks like a small tournament bracket. Each parent has priority no worse than its children, so the best item is always at the root. Java stores that tree in a flat Object[], which is why there are no node objects or left/right references.

The picture below is the one to keep in your head. The tree shape is implicit in the array indexes.

Fig. 1

A binary min-heap shown as both a tree and an array, where parent i has children 2i+1 and 2i+2 and the smallest value is at index 0

A binary min-heap shown as both a tree and an array, where parent i has children 2i+1 and 2i+2 and the smallest value is at index 0

Notice what the heap does not promise. A parent is no larger than its children, but siblings are not sorted against each other, and the left subtree is not globally smaller than the right subtree. That is enough to find the minimum quickly, not enough to iterate in sorted order.

02PriorityQueue under the hood

PriorityQueue keeps its elements in an Object[] queue and a size. The default initial capacity is 11. The array grows when needed, but the logical heap is only the first size slots.

Try it yourself first

Before reading the output, add these numbers by hand: 40, 10, 30, 5. Which value must end up at index 0, and which swaps happen when 5 is offered?

import java.util.Arrays;
import java.util.PriorityQueue;

PriorityQueue<Integer> queue = new PriorityQueue<>();
queue.offer(40);
queue.offer(10);
queue.offer(30);
queue.offer(5);

System.out.println(queue.peek());              // 5
System.out.println(queue);                     // one heap-shaped order, not a sorted contract

while (!queue.isEmpty()) {
    System.out.print(queue.poll() + " ");      // 5 10 30 40
}

offer(5) appends at the first free slot, then compares with its parent. Starting from [10, 40, 30], 5 lands at index 3. Its parent is index 1 (40), so they swap. Now 5 is at index 1, whose parent is index 0 (10), so they swap again. The root becomes 5.

Notice three things:

  • peek() reads queue[0], so it is O(1).
  • poll() repeatedly returns sorted values because each call repairs the heap before the next one.
  • toString() and iteration walk the internal array. That order is not sorted and should not be used as a sorted result.

Here is a tiny heap implementation with the same index math. I like showing this version because it removes the API surface and leaves the invariant visible.

import java.util.ArrayList;
import java.util.List;

final class IntMinHeap {
    private final List<Integer> heap = new ArrayList<>();

    void offer(int value) {
        heap.add(value);
        siftUp(heap.size() - 1);
    }

    int peek() {
        if (heap.isEmpty()) throw new IllegalStateException("empty heap");
        return heap.get(0);
    }

    int poll() {
        if (heap.isEmpty()) throw new IllegalStateException("empty heap");
        int result = heap.get(0);
        int last = heap.remove(heap.size() - 1);
        if (!heap.isEmpty()) {
            heap.set(0, last);
            siftDown(0);
        }
        return result;
    }

    private void siftUp(int index) {
        int value = heap.get(index);
        while (index > 0) {
            int parent = (index - 1) >>> 1;
            int parentValue = heap.get(parent);
            if (parentValue <= value) break;
            heap.set(index, parentValue);
            index = parent;
        }
        heap.set(index, value);
    }

    private void siftDown(int index) {
        int value = heap.get(index);
        int half = heap.size() >>> 1; // first leaf index is size / 2
        while (index < half) {
            int left = (index << 1) + 1;
            int right = left + 1;
            int child = left;
            int childValue = heap.get(left);

            if (right < heap.size() && heap.get(right) < childValue) {
                child = right;
                childValue = heap.get(right);
            }
            if (value <= childValue) break;
            heap.set(index, childValue);
            index = child;
        }
        heap.set(index, value);
    }
}

Premium lesson

Keep reading with the Java Pass

The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.

  • Offer and poll, step by step
  • Advanced: comparators, iteration, heapify and updates
  • Edge cases you should say out loud
  • How to say it in the interview

From ₹199 · one-time payment · No auto-renewal

Practise this topicQueues & deques: interview questions & scenarios
Further reading (5)