Concepts / Priority Queues

Priority Queues

A PriorityQueue can sort a collection: add all items with addAll, then remove them one at a time until the queue is empty.

  • Programming
Interactive lab

Try it: Stack and Queue

The difference between a stack (last in, first out) and a queue (first in, first out).

How it works

  1. A stack adds (push) and removes (pop) at the same end, the top — the last item added leaves first.
  2. A queue adds (enqueue) at the back and removes (dequeue) from the front — the first item added leaves first.
  3. peek / front look at the next item without removing it.
  4. Adding to a full structure is an overflow; removing from an empty one is an underflow.

Default run (3 steps): Stack (bottom → top): 4, 7. 2 of 8 slots used. Choose an operation. Example: push 9. … Stack is now: 4, 7, 9. Size 3/8.

Simplified: Fixed capacity of 8 items so overflow can be shown; Python lists and collections.deque grow as needed.

Educational simulation

Loading the simulation…

A Second Route to Sorted Output

Earlier in the course you saw that a TreeSet can sort the items in a collection and remove duplicates along the way. A PriorityQueue offers a second route to sorted output. Instead of asking a set to keep its items ordered, you place every item into a priority queue and then take the items back out one at a time. The questions this article answers are simple: what order do the items come out in, and what happens to duplicates when you do this?

The technique has two stages. In the first stage, every item is added to the queue. In the second stage, items are removed one at a time inside a loop until the queue is empty. The sorting does not come from the queue handing you a finished list. It comes from the rule that governs each removal, and the rest of this article builds up that rule step by step.

What a Priority Queue Actually Promises

Definition

A priority queue is an abstract data type that represents a collection of items. Each item has an assigned priority, which allows any two items to be compared. Its remove operation returns the item with the minimum priority currently present in the queue, not the item that arrived first.

Many learners quietly assume that a priority queue is just an ordinary queue, where the item added first leaves first and the item added last waits its turn. That assumption feels natural because the word queue suggests a line of people. A priority queue follows a different rule. Position in the line does not decide which item comes out. The priority does.

The Three Essential Operations

The Java PriorityQueue class is written as a parameterized class, PriorityQueue<T>, which implements a priority queue of objects of type T. The class also implements the Collection<T> interface, so a priority queue object has every method that interface defines. The operations essential to a priority queue, however, are only three: add, remove, and isEmpty.

  1. 1
    add places an item into the queue. During the sorting technique, this is the first stage, where addAll puts every item in the collection into the queue.
  2. 2
    isEmpty reports whether the queue currently holds any items. It is the check that controls the draining loop.
  3. 3
    remove takes out and returns the item with the minimum priority currently in the queue. Each call removes one item, so the queue shrinks by one each time.

Predicting the Removal Order

Before you trace the mechanism, make a prediction. Suppose a collection holds the strings pear, apple, banana, and apple, in that order. The first stage adds all four strings to the queue. The second stage removes them one at a time until the queue is empty. Decide what the printed output will be, paying close attention to whether both copies of apple appear.

What do you think happens?

Given pear, apple, banana, and apple added in that order, what order do the four remove calls return?

Reveal answer
Answer:

apple, apple, banana, pear

Strings are removed in lexicographic order, and the queue keeps duplicate items, so both copies of apple appear before banana and pear. The insertion order plays no part in the output.

java
Output
apple
apple
banana
pear
Notes on the code
  • addAll is the first stage: every item in coll goes into the queue.
  • isEmpty controls the loop, so remove is only called while items remain.
  • Each remove returns the item with the minimum priority still waiting, which for strings is the next item in lexicographic order.
Worked example

Tracing the drain loop for pear, apple, banana, apple

Trace what each call to remove returns, and find the step where a predicted sequence that treats the queue like a plain FIFO line stops matching the real output.

  1. 1
    Stage one: addAll

    All four strings enter the queue. Their insertion order is pear, apple, banana, apple, but the queue does not store them as a line to be served in that order.

  2. 2
    Remove call 1

    The minimum-priority item still waiting is apple. The first copy is printed.

  3. 3
    Remove call 2

    The second copy of apple is still waiting, so it is the minimum and is printed. A prediction that drops the duplicate diverges here, because the queue does not discard items.

  4. 4
    Remove call 3

    banana is now the minimum and is printed.

  5. 5
    Remove call 4 and the check

    pear is the last item and is printed. isEmpty now returns true, so the loop ends.

Result

The printed output is apple, apple, banana, pear. A plain FIFO prediction would print pear, apple, banana, apple, and it diverges at the very first line.

remove call 1appleremove call 2appleremove call 3bananaremove call 4pearisEmpty checktrue, loop endsnextnextnextqueue empty
After addAll puts the four items in the queue, in what order does each remove return them, and at which step would a predicted sequence stop matching the real output?

The diagram shows the loop as a sequence of remove calls, each followed by the isEmpty check implied by the loop. The second call is where a careless prediction usually fails. A learner who expects each distinct value to appear only once will write apple, banana, pear and stop there. The queue keeps both copies, so the second apple is printed as well.

Why Items Must Be Comparable

A remove operation that returns the minimum item only makes sense if the queue can decide, for any two items, which one is smaller. The source compares this to sorting: we need to be able to compare any two items in the queue. Without a way to make that comparison, the queue has no basis for choosing which item comes out next.

Java supports this comparison in two ways. The first is to have the item's class implement the Comparable interface and its compareTo() method. The second is to supply a Comparator to the PriorityQueue constructor. Both ways mirror the two standard approaches to sorting objects, and both give the queue the decision it needs.

The source names String, Integer, and Date as classes that implement Comparable. Objects of these classes can be used directly in a priority queue, which is what makes the sorting technique work without extra setup. The practical rule follows: if the elements you want to sort belong to a class that implements Comparable, you can add them to a PriorityQueue and remove them to get sorted output. The source does not describe how to sort elements of a class that does not implement Comparable, so this article makes no claim about that case.

Comparing a Priority Queue With a Plain Queue

Consider the same insertion sequence fed into two structures. A plain first-in, first-out queue returns items in the order they were added, so pear, apple, banana comes out as pear, apple, banana. A priority queue given the same insertion sequence returns the minimum-priority item at each step. For strings, that produces lexicographic order, so the output becomes apple, banana, pear.

pearremoved firstappleremoved firstappleremoved secondbananaremoved secondbananaremoved thirdpearremoved third
Given the same insertion sequence, how does the removal order of a priority queue differ from a plain FIFO queue?

The two columns in the figure hold the same three values, but the removal order differs. The FIFO column follows arrival order, while the priority queue column follows priority. This difference is the core idea of the whole article: a priority queue is not a line that serves people in turn.

Sorting With a PriorityQueue Versus a TreeSet

The source presents the PriorityQueue technique as a companion to the TreeSet technique. Both can sort a collection, but they differ on duplicates. A TreeSet removes duplicates as it sorts. The PriorityQueue technique keeps duplicate items in the output. Comparing the two on the same input makes this difference easy to see.

QuestionPriorityQueue techniqueTreeSet technique
How items are sortedAdded with addAll, then removed one at a timeKept in order by the set as items are added
Duplicates in the inputKept in the outputRemoved along the way
Output for pear, apple, banana, appleapple, apple, banana, pearapple, banana, pear
Ordering supplied byComparable or a ComparatorComparable or a Comparator

The duplicate behavior is the key difference between the two sorting techniques.

applefirst copyappleone copyapplesecond copybananaone copybananaone copypearone copypearone copy
When the input contains duplicate values, what does each structure keep and what does each output, side by side?

The figure makes the difference concrete. The priority queue holds and outputs four items, including both copies of apple. The TreeSet outputs three, because the duplicate apple is removed. If your goal is a sorted list that preserves every item, the priority queue technique matches that goal. If your goal is a sorted list of distinct values, the TreeSet is the better fit.

Two Implementation Strategies and Their Costs

The source describes two broad ways to implement a priority queue. The simplest uses a linked list that keeps items in order of increasing priority. In that design, remove is simple, because it removes and returns the first item in the list. Add is the expensive part, because the new item must be inserted into its correct position. The average run time for add in this design is Theta(n), where n is the number of items in the list.

The source also states that priority queues can be implemented so that both add and remove have run time Theta(log(n)), which is much more efficient. That efficient version uses a heap. The source does not explain how a heap arranges its items and says it will not discuss that implementation in the section, so this article does not describe the arrangement. What you can rely on is the complexity contrast between the two designs.

ImplementationCost of addCost of remove
Linked list kept in priority orderTheta(n) on averageSimple removal of the first item
Heap-based implementationTheta(log(n))Theta(log(n))

Both designs support the same removal rule. They differ in how much work add and remove cost.

Two Meanings of the Word Heap

The word heap appears in this topic in a specific sense. A heap is the data structure that an efficient priority queue implementation uses. It is distinct from the other memory the word is often used for, which is the area where objects are created while a program runs. A learner should keep these apart. The heap data structure is an internal technique for keeping a priority queue's ordering efficient. The objects stored in the queue are separate items that the queue refers to.

Common Mistakes With Priority Queues

  • Common mistake

    Predicting that a priority queue returns items in insertion order

    What it looks like

    Adding pear, apple, banana and expecting pear to come out first.

    Why it fails

    The remove operation returns the item with the minimum priority among those waiting, not the item that arrived first.

    Do this instead

    For strings, expect lexicographic order: apple, banana, pear.

  • Common mistake

    Dropping the second copy of a duplicate value

    What it looks like

    Predicting apple, banana, pear for the input pear, apple, banana, apple.

    Why it fails

    A PriorityQueue keeps duplicate items, unlike a TreeSet, so both copies of apple are removed.

    Do this instead

    Predict apple, apple, banana, pear and count the removals.

  • Common mistake

    Calling remove on an empty queue

    Why it fails

    In Java, remove throws an exception when the queue is empty.

    Do this instead

    Guard the loop with isEmpty, so that remove runs only while items remain.

  • Common mistake

    Adding objects with no way to compare them

    Why it fails

    The queue must decide which of two items is smaller, and it cannot do that without a comparison.

    Do this instead

    Use a class that implements Comparable, or supply a Comparator to the PriorityQueue constructor.

Practice: Predict Before You Check

MEDIUM

A collection holds the strings kiwi, cherry, kiwi, apple, in that order. Using the add-all-then-remove-until-empty technique, write down the exact sequence of strings the loop prints. Then state whether the output contains kiwi once or twice, and explain why.

Hints
  • Ask first what the removal rule says about which item comes out on each remove call.
  • Count the copies of kiwi in the input before you count them in your predicted output.
  • Compare your predicted list with what the TreeSet technique would print, and note the difference in duplicates.

Key Takeaways

  • A PriorityQueue sorts a collection by using addAll to add every item and then calling remove, inside a loop guarded by isEmpty, until the queue is empty.
  • A priority queue's remove returns the item with the minimum priority currently present, which is not the same as a first-in, first-out queue.
  • For strings, Integer, and Date, which implement Comparable, the queue output is in order, and duplicates are kept in the output, unlike a TreeSet.
  • Items must be comparable, through the Comparable interface and compareTo(), or through a Comparator supplied to the PriorityQueue constructor.
  • A linked-list design makes add expensive at Theta(n) on average, while a heap-based design achieves Theta(log(n)) for both add and remove.