ZetCode

C# PriorityQueue

last modified October 5, 2026

C# PriorityQueue tutorial shows how to use the PriorityQueue<TElement, TPriority> collection in .NET.

A priority queue stores elements together with priorities. Unlike a regular queue, which removes elements in the order they were added, a priority queue removes an element with the smallest priority first by default. The collection is part of System.Collections.Generic and is available starting with .NET 6.

The .NET priority queue is implemented as a quaternary min-heap. It does not keep all elements in sorted order; it organizes them so that the element with the minimum priority can be found efficiently.

C# PriorityQueue example

In the first example, we enqueue jobs with integer priorities and process them from the smallest priority value to the largest.

Program.cs
using System;
using System.Collections.Generic;

var jobs = new PriorityQueue<string, int>();

jobs.Enqueue("Generate monthly report", 3);
jobs.Enqueue("Process payment", 2);
jobs.Enqueue("Database alert", 1);

while (jobs.TryDequeue(out var job, out var priority))
{
    Console.WriteLine($"{priority}: {job}");
}

The first generic type is the element type, and the second is the priority type. Each call to Enqueue supplies both values.

var jobs = new PriorityQueue<string, int>();

We create a queue of string jobs whose priorities are integers. The default priority comparer orders integers in ascending order, so a smaller number means that a job is processed sooner.

while (jobs.TryDequeue(out var job, out var priority))

TryDequeue removes the element with the smallest priority and returns both the element and its priority. It returns false when the queue is empty, so the loop ends without an exception.

$ dotnet run
1: Database alert
2: Process payment
3: Generate monthly report

C# PriorityQueue operations

Peek reads the element with the smallest priority without removing it. TryPeek is useful when the queue might be empty. Dequeue removes and returns the minimum-priority element, and Count gives the number of queued elements.

Program.cs
using System;
using System.Collections.Generic;

var queue = new PriorityQueue<string, int>();
queue.Enqueue("Write tests", 2);
queue.Enqueue("Fix production issue", 1);

Console.WriteLine($"Next: {queue.Peek()}");
Console.WriteLine($"Queued: {queue.Count}");

if (queue.TryDequeue(out var task, out var priority))
{
    Console.WriteLine($"Started {task} (priority {priority})");
}

Console.WriteLine($"Remaining: {queue.Count}");

Peek leaves the queue unchanged. If an empty queue is possible, use TryPeek or TryDequeue instead of Peek or Dequeue; the non-try methods throw InvalidOperationException when no element is available.

$ dotnet run
Next: Fix production issue
Queued: 2
Started Fix production issue (priority 1)
Remaining: 1

C# PriorityQueue with a custom comparer

The default comparer makes a min-priority queue: lower values come out first. Pass an IComparer<TPriority> to the constructor to change that order. This example uses a comparer that puts larger numbers first.

Program.cs
using System;
using System.Collections.Generic;

var comparer = Comparer<int>.Create((left, right) => right.CompareTo(left));
var scores = new PriorityQueue<string, int>(comparer);

scores.Enqueue("Ada", 84);
scores.Enqueue("Linus", 97);
scores.Enqueue("Grace", 91);

while (scores.TryDequeue(out var name, out var score))
{
    Console.WriteLine($"{score}: {name}");
}

The comparer reverses the usual integer order. Consequently, the queue now removes the entry with the highest score first.

$ dotnet run
97: Linus
91: Grace
84: Ada

Equal priorities and stable ordering

The queue does not guarantee first-in, first-out ordering for elements with equal priorities. If equal-priority items must retain their insertion order, include a sequence number as a secondary part of the priority.

Program.cs
using System;
using System.Collections.Generic;

var tasks = new PriorityQueue<string, (int Rank, long Sequence)>();
long sequence = 0;

tasks.Enqueue("First normal task", (2, sequence++));
tasks.Enqueue("Urgent task", (1, sequence++));
tasks.Enqueue("Second normal task", (2, sequence++));

while (tasks.TryDequeue(out var task, out var priority))
{
    Console.WriteLine($"rank {priority.Rank}: {task}");
}

Value tuples are compared field by field. The rank is considered first, then the sequence number breaks ties, so tasks at the same rank are processed in the order they were enqueued.

$ dotnet run
rank 1: Urgent task
rank 2: First normal task
rank 2: Second normal task

C# PriorityQueue details

The priority type does not have to be numeric. It can be any type that can be ordered by the default comparer, or by a comparer supplied to the constructor. For example, a date can be used to process the nearest deadline first.

The queue is not synchronized for concurrent access. If multiple threads need to modify the same instance, protect those operations with suitable synchronization or use a concurrent design.

Source

PriorityQueue<TElement, TPriority> Class - Microsoft Learn

In this article we have worked with the PriorityQueue<TElement, TPriority> collection in C#.

Author

Jan Bodnar writes programming tutorials and educational content.

List all C# tutorials.