Why should we care about the capacity of List<T>?

generic-list

List<T> is a strongly typed list of objects in C# which is pretty hands-on and straightforward. It provides many capabilities namely storing, searching and manipulating lists. The generic list is a wrapper around an array whose size grows dynamically and facilitates you to work on collections. Let's look inside and reveal its details.

Generic lists comes under System.Collections.Generic namespace. You can see the source code in here.

Table of contents:

Constructors

List<T> has three overloaded constructors which are described in the below table.

Constructors Description
public List() Constructs a List. The list is initially empty and has a capacity of zero. Upon adding the first element to the list the capacity is increased to DefaultCapacity and then increased in multiples of two as required.
public List(int capacity) Constructs a List with a given initial capacity. The list is initially empty but will have room for the given number of elements before any reallocations are required.
public List(IEnumerable<T> collection) Constructs a List, copying the contents of the given collection. The size and capacity of the new list will both be equal to the size of the given collection.

As already mentioned, generic lists use an internal array as a data structure to store data. Whenever you create a new instance of List<T> you can specify the Capacity of the list in the constructor, as a result, the argument is considered as the internal array length. If you do not specify it, the DefaultCapacity would be chosen as a default value which is four.

private const int DefaultCapacity = 4;

Now, two questions may pop into your mind:

  • What happens if I add more than four items to the list?
  • How does List<T> handle the capacity of the internal array?

Capacity vs Count

Before answering the questions, let me explain these two concepts. The Capacity indicates the maximum amount of items that the internal array can contain however the Count shows the size of the internal array which means how many items exist in it.

For example in the code below, there is only one item in the list whose capacity is four. Since I do not specify the capacity of the list, the default capacity is intended.

List<int> list = new List<int>();
list.Add(1);
Console.WriteLine($"The count is: {list.Count}");
Console.WriteLine($"The capacity is: {list.Capacity}");

//The Result of the example is:
    //The count is: 1
    //The capacity is: 4

Add a new item to the list

Let's get back to the questions. Before adding a new element, the size of the internal array is checked, if the size is greater than the capacity, the capacity of the list is doubled. Indeed, the capacity is increased to twice the current capacity. having changed the capacity, the current items are copied to a new array with a new length as many as the doubled capacity.

In the code below, you can see how the capacity of the list would be doubled after the fifth item adds to the list.

List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
Console.WriteLine($"The count is: {list.Count}");
Console.WriteLine($"The capacity is: {list.Capacity}");
list.Add(5);
Console.WriteLine($"The count is: {list.Count}");
Console.WriteLine($"The capacity is: {list.Capacity}");

//The Result of the example is:
    //The count is: 4
    //The capacity is: 4
    //The count is: 5
    //The capacity is: 8

Bear in mind that whenever the capacity is exceeded, the above story will happen and the new capacity always is multiplied by 2.

Why should you always set the capacity of List<T>?

Simply put, you should always indicate the capacity in the constructor even if you have only a rough estimation on how many items will be added to the list because relocating the internal array into a new one with a new length is just wasting the resources and the memory stuffed with many useless arrays that must be reclaimed by the GC which has a cost on the performance of your program.

Sum up

This article goes over the generic lists and related concepts such as Capacity and Count. We learned how data is stored in a list under the hood. Furthermore, we figured out how to use generic lists efficiently in terms of memory management.