Java PriorityQueue — Mutating Fields Breaks Heap Order
PriorityQueue does not re-heapify after object field changes; low-priority tasks run before high-priority.
- Java's PriorityQueue is a min-heap by default — poll() returns the smallest element.
- Pass a Comparator to change ordering; use Comparator.reverseOrder() for a max-heap.
- Iteration does not produce sorted order — only poll() and peek() respect the heap ordering.
- offer() and poll() are O(log n); peek() is O(1); contains() is O(n).
- Production trap: iterating the queue assuming sorted order will give incorrect results. Always poll() repeatedly.
- Not thread-safe — use PriorityBlockingQueue for concurrent access.
A PriorityQueue is like a line where the most important person (lowest number) goes first, no matter when they joined. You can change what 'important' means by providing a Comparator.
Basic Min-Heap Usage
The code demonstrates two common patterns: the default min-heap and the max-heap using Comparator.reverseOrder(). In the min-heap, poll() returns the smallest element each time. The max-heap reverses the natural ordering so the largest element comes out first. The internal representation is a binary heap array, which means the elements are not fully sorted — only the root is guaranteed to be the smallest (or largest for max-heap).
Comparator.reverseOrder() for max-heap.Custom Objects with Comparator
When your objects don't have a natural ordering, you provide a Comparator. The example uses a Task record with priority and deadline fields. The comparator sorts by priority ascending, then by deadline ascending. This is typical for job schedulers where you want to process urgent tasks (lower priority number) first, and tie-break by deadline. Note that if the comparator is inconsistent with equals (i.e., compare returns 0 when equals returns false), the queue may misbehave during remove(Object) or contains() operations.
poll() results — critical tasks may be skipped.Dijkstra's Shortest Path — Classic PriorityQueue Use
Dijkstra's algorithm is the textbook use case for PriorityQueue. We maintain a min-heap of [distance, node] pairs. At each step, we poll the node with the current shortest distance. If we find a shorter path to a neighbor, we update the distance and push the updated pair onto the heap. The stale entry check (if d > dist[u]) is crucial: old entries with larger distances are ignored, preventing the algorithm from processing obsolete states.
PriorityQueue Internal Structure and Performance
Java's PriorityQueue is backed by a dynamic array (Object[] queue) that stores elements in a binary heap structure. The heap property ensures that the parent is less than or equal to its children (for the default comparator). This structure allows O(log n) insertion (offer) and O(log n) removal of the root (poll). However, contains() and remove(Object) require a linear scan of the array (O(n)) because the heap is not sorted. The initial capacity is 11, and it grows by 50% when full, which involves copying the array.
Contains() is O(n) — do not call it in a hot path. If you need fast membership checks, maintain a separate HashSet.poll() over contains() for performance.PriorityQueue vs TreeSet vs Arrays.sort() — When to Use Which
- PriorityQueue: dynamic, allows duplicates, O(log n) insert/delete of the smallest element, no in-order iteration.
- TreeSet: sorted set, no duplicates, O(log n) insert/delete, supports in-order iteration and range queries.
Arrays.sort(): one-shot sorting of a fixed collection, O(n log n), no dynamic operations.
Use PriorityQueue when you need a dynamic priority-processing pipeline with possible duplicates (e.g., job scheduler). Use TreeSet when you need an sorted set without duplicates and you need to iterate in order or query subsets. Use Arrays.sort() when you have a fixed list that you need sorted once.
Arrays.sort() is fine for immutable lists but not for a dynamic queue where elements arrive over time.Arrays.sort() for one-time sorting of a static collection.Mutating Priority Fields in a PriorityQueue Breaks Ordering
- Never mutate objects while they are in a PriorityQueue.
- If updates are needed, remove and re-add the object.
- Consider using a separate data structure for mutable priority tasks.
- Always test your queue logic with mutation scenarios.
poll() in a loop to get sorted order.Key takeaways
Comparator.reverseOrder() for a max-heap; use Comparator.comparing() for custom objects.poll() respects the heap ordering.peek() returns without removing; offer()/add() inserts.Common mistakes to avoid
4 patternsMutating priority fields while object is in queue
Assuming iteration order is sorted
poll() in a loop to extract elements in sorted order, or copy to a list and sort with Collections.sort().Using PriorityQueue without a Comparator for a custom class that does not implement Comparable
Not pre-sizing the queue when many elements are added
Interview Questions on This Topic
How do you create a max-heap using Java's PriorityQueue?
Comparator.reverseOrder() as the constructor argument: new PriorityQueue<>(Comparator.reverseOrder()). This reverses the natural ordering, so the largest element is at the root.Frequently Asked Questions
That's Collections. Mark it forged?
3 min read · try the examples if you haven't