MacMusic  |  PcMusic  |  440 Software  |  440 Forums  |  440TV  |  Zicos
collections
Search

How to use frozen collections in C#

Thursday June 12, 2025. 11:00 AM , from InfoWorld
How to use frozen collections in C#
Developers often grapple with the available options in their quest to use the most suitable data structure for their application. The various collections in C# offer different ways to store data objects and search, sort, insert, modify, delete, and iterate across those objects. For example, the.NET Base Class Library (BCL) provides support for read-only, immutable, and frozen collections. Each of these collection types has distinct use cases.

Read-only collections provide read-only access to data in a mutable collection, i.e., a collection that can be altered by other means. Immutable collections are collections that preserve their structure, meaning they cannot be altered after creation. An immutable collection enables thread-safe modifications to the data by creating a new instance of the collection each time you change its data. Typical examples of immutable collections are ImmutableStack, ImmutableList, and ImmutableHashSet.

Frozen collections, including the FrozenSet and FrozenDictionary classes, were introduced in.NET 8. These are immutable collections that have been optimized for fast look-ups. In addition, frozen collections provide a thread-safe way to access an immutable collection. In this article, we’ll examine how we can use frozen collections in C#. We’ll also look at the performance advantages of frozen collections by comparing the look-up speed of a FrozenSetto other collection types.

Create a console application project in Visual Studio

First off, let’s create a.NET Core console application project in Visual Studio. Assuming Visual Studio 2022 is installed in your system, follow the steps outlined below to create a new.NET Core console application project.

Launch the Visual Studio IDE.

Click on “Create new project.”

In the “Create new project” window, select “Console App” from the list of templates displayed.

Click Next.

In the “Configure your new project” window, specify the name and location for the new project.

Specify the Solution name for your project and select the check box to place the solution and the project files in the same directory.

Click Next.

In the “Additional information” window shown next, choose “.NET 9.0 (Standard Term Support)” as the framework version you would like to use.

Ensure that the check boxes for enabling container support, specifying container build type, and enabling native AOT publish are unchecked. We won’t be using any of these features in this example.

Click Create.

We’ll use this.NET 9 Core console application project to work with frozen collections in the subsequent sections of this article.

What are frozen collections? Why do we need them?

Frozen collections refer to a specific type of collection that blends the benefits of immutability and fast read performance. The term immutable here implies that these collections cannot be altered after they are created, thereby ensuring thread safety. Frozen collections eliminate the need to synchronize code that uses these collections in concurrent applications, i.e., applications that handle multiple threads simultaneously.

The System.Collections.Frozen namespace in.NET 8 introduces two new collection classes, FrozenSet and FrozenDictionary. “Frozen” here implies that the collections are immutable. You cannot change these collections once an instance of these classes has been created. These new collection classes enable you to perform faster look-ups and enumerations using methods such as TryGetValue() and Contains().

Key features of frozen collections in.NET Core

These are the key features of frozen collections in.NET Core:

Immutability: Because frozen collections are immutable, you cannot alter them after they are created. As a result, your application’s data will remain thread-safe and consistent without requiring the use of synchronization techniques.

Performance optimization: Frozen collections allow only read access, which reduces their memory footprint or overhead during initialization while improving lookup times. Hence, these collections are a great choice when your application needs frequent read-only access to data.

Thread safety: Thanks to their immutable nature, frozen collections are thread-safe, allowing multiple threads to access the same frozen collection simultaneously without encountering race conditions or synchronization issues.

Simplicity: Frozen collections are easy to use. You can easily integrate frozen collections into your application because their APIs are the same as the APIs of traditional collections.

Types of frozen collections in.NET Core

Basically, there are two types of frozen collections in.NET Core:

FrozenDictionary: A FrozenDictionary represents a read-only dictionary that is optimized for fast searches. You can use this collection when the collection needs to be created once and read frequently.

FrozenSet: A FrozenSet represents a read-only immutable set optimized for fast searches and enumeration. Like a FrozenDictionary, you cannot alter this collection after creation.

Because both FrozenSet and FrozenDictionary are read-only collections, you cannot add, change, or remove items in these collections.

Create frozen collections in C#

The following code snippet shows how you can create a FrozenSet from a HashSet instance, populate it with sample data, and then search for an item inside it. Recall that a HashSet is an unordered collection of unique elements that supports set operations (union, intersection, etc.) and uses a hash table for storage.

var hashSet = new HashSet { 'A', 'B', 'C', 'D', 'E' };
var frozenSet = hashSet.ToFrozenSet();
bool isFound = frozenSet.TryGetValue('A', out _);

In the preceding code snippet, the ToFrozenSet() method is used to convert a HashSet instance to a FrozenSet instance. The method TryGetValue() will return true in this example because the data searched for is present in the collection.

The following code shows how you can create a FrozenDictionary, store data in it, and search for a particular key in the collection.

var dictionary = new Dictionary
{
{ 1, 'A' },{ 2, 'B' },{ 3, 'C' },{ 4, 'D' },{ 5, 'E' }
};
var frozenDictionary = dictionary.ToFrozenDictionary();
bool isFound = dictionary.TryGetValue(7, out _);

When the above code is executed, the TryGetValue method will return false because the key 7 is not available in the collection.

Benchmarking performance of frozen collections in.NET Core

Let us now benchmark the performance of a frozen collection, specifically a FrozenSet, against other collection types using the BenchmarkDotNet library. For comparison, we’ll use a List, a HashSet, and an ImmutableHashSet.

Recall that a List is a mutable, strongly-typed, dynamically-sized, ordered collection of elements (even duplicates), and that a HashSet is a mutable, array-based collection of unique elements that is optimized for fast look-ups. Note that the time complexity for searching an item in a HashSet is O(1), compared to a time complexity of O(n) for a List (where n is the number of elements in the collection). Clearly, a HashSet is useful in cases where quick access is necessary. On the downside, a HashSet consumes more memory than a List and cannot include duplicate elements. You should use a List if you want to store an ordered collection of items (possibly duplicates) and where resource consumption is a constraint.

An ImmutableHashSet is the immutable counterpart of a HashSet, i.e., an array-based collection of unique elements that cannot be changed once created. Note that, whereas a HashSet is a mutable, array-based hash bucket optimized for fast searches, an ImmutableHashSet is a persistent data structure that uses AVL trees for immutability. Because this introduces additional overhead, searches of an ImmutableHashSet are much slower than searches of a HashSet.

Install BenchmarkDotNet

To use BenchmarkDotNet, you must first install the BenchmarkDotNet package. You can do this either via the NuGet Package Manager in the Visual Studio IDE, or by executing the following command at the NuGet Package Manager Console:

Install-Package BenchmarkDotNet

Create a benchmark class

In the console application project we created earlier, create a class named CollectionPerformanceBenchmark in a file with the same name and a.cs extension as shown below.

[MemoryDiagnoser]
public class CollectionPerformanceBenchmark
{
[GlobalSetup]
public void SetupData()
{
}
}

In the preceding code snippet, the attribute MemoryDiagnoser has been attached to the CollectionPerformanceBenchmark class to include memory consumption metrics in our benchmark results. The SetupData method is where we will write the initialization code for our benchmark, i.e., the code to create our collections, populate them with dummy data, and so on. The [GlobalSetup] attribute is used to ensure that SetupData method is executed before any benchmark methods.

Write the initialization code

The following code snippet implements the SetupData method that initializes each of the collection instances and populates them with data.

[GlobalSetup]
public void SetupData()
{
list = Enumerable.Range(0, NumberOfItems).ToList();
hashSet = Enumerable.Range(0, NumberOfItems).ToHashSet();
immutableHashSet = Enumerable.Range(0, NumberOfItems).ToImmutableHashSet();
frozenSet = Enumerable.Range(0, NumberOfItems).ToFrozenSet();
}

Write the benchmark code

Next, we must write the necessary code for benchmarking. Each method that contains code for benchmarking should be decorated using the [Benchmark] attribute as shown below.

[Benchmark(Baseline = true)]
public void SearchList()
{
for (var i = 0; i

In the preceding code snippet, the SearchList method has been decorated using the [Benchmark] attribute and has been set as the baseline against which the performance of the other benchmark methods will be compared.

The complete source code of our CollectionPerformanceBenchmark benchmarking class is given below for reference. Note that it includes the necessary code for the other benchmark methods (SearchFrozenSet, SearchHashSet, and SearchImmutableHashSet), which are implemented in much the same way we implemented the SearchList method.

[MemoryDiagnoser]
public class CollectionPerformanceBenchmark
{
private const int NumberOfItems = 1000;
private List list;
private FrozenSet frozenSet;
private HashSet hashSet;
private ImmutableHashSet immutableHashSet;

[GlobalSetup]
public void SetupData()
{
list = Enumerable.Range(0, NumberOfItems).ToList();
hashSet = Enumerable.Range(0, NumberOfItems).ToHashSet();
immutableHashSet = Enumerable.Range(0, NumberOfItems).ToImmutableHashSet();
frozenSet = Enumerable.Range(0, NumberOfItems).ToFrozenSet();
}

[Benchmark(Baseline = true)]
public void SearchList()
{
for (var i = 0; i

Faster searches in frozen collections

When you run the benchmarks above, you’ll notice the distinct performance differences between searching data in a frozen collection (in this case a FrozenSet) compared to a normal collection such as a List as shown in Figure 1.

Figure 1. Performance benchmark results for searching various collection types (List, FrozenSet, HashSet, and ImmutableHashSet). IDG

As you can see in the benchmark results, searching a FrozenSet is not only much faster than searching a List, it is even faster than searching a HashSet. So, when you need fast look-ups in a mutable set, use a HashSet. When you need fast look-ups in an immutable set, use a FrozenSet. Keep in mind that, because a FrozenSet is immutable, it incurs a significantly higher overhead cost of creation compared to a HashSet.

Performance and scalability have improved with every release of.NET Core. The introduction of frozen collections heralds a new approach to efficiently searching data, and future releases will include more such improvements.
https://www.infoworld.com/article/4000458/how-to-work-with-frozen-collections-in-c-sharp.html
News copyright owned by their original publishers | Copyright © 2004 - 2025 Zicos / 440Network
Current Date
Jun, Fri 13 - 17:56 CEST