Introduction to C# Lists

List Banner Image

Introduction

C# lists are the backbone of collections and often the most used. They are verstile and flexable. A step up from the standard array because list and increase and decrease the underline array automaticly. So it hard to go wrong with use a list. They are a go to data structure with it's design although not perfect there are other datastructures that complement it's design and weaknesses. List is a one of the most use collections and for good reason. When working with data it is a natural fit to add them to lists. Data could be from a database, text files, user input, drop down menu, etc. Since c# is a strongly type language, you need to know ahead of time what type of data you will be storing into the list. If working with text then you'll need to make list of strings. If working with some numerical values then make a list of ints or doubles. Typically, receiving the values from a list involves looping through with a foreach or for. Or if it's known which position the value is located in the list then an index value can be passed to retrieve the value immediately. This is possible since the underlying data structure of a list is a array which expands and decreased automatically. All these reasons make list a great collection type and one if not the most used in the language.

What Are Lists In C#?

Lists are a collection of a type in which the underlying data structure is an array. Even though an array is of fixed length at it's creation. The list manages the array so it grows when when the lists needs to grow. This enables list items to be accessed by an index for fast access, add items without being concerned about a limit, remove, search and sort items. Lists also allow duplicates items and the order is preserved as we add items.

Why Do We Need Lists?

Lists are needed for holding a group of items of different types, sorting, searching or even looping through. For example, lists could hold different names that are pulled from the database to display on the UI.

Are Lists An Array?

A list is an array but lists manages an array so you don't need to or be concerned about array issues such as the size of the list or adding and removing items. Lists handles all these operations seemlessly. An array is a fixed sized and adding them or removing would take us writing code to do.

How To Create A List?

Lists can be created with the following syntax.

List<string> names = new List<string>();

Take notice of each of the elements in this statement.

  • List - This is the class name. List is one of the collection class types
  • string - This is the type. Generics in C# require a type because it is a strongly type langauge so it needs to know the type at compile time. This type could be int, double, bool, or even class you create.
  • names - The variable or what this list is refered to in the code. We can add, remove, and apply all the list operations. For example, names.Add("Bob"); will add the name 'Bob' to the list.
  • new - This keyword is telling the compiler that a new list will be added in memory so the memory allcation is handle behind the scenes.

Types of all kinds can be created but you can only add an object to the list if it is that type or inherits from that type. See example below.

Create List Code Syntax Example
List<string> textList = new List<string>();//Can hold multiple strings
List<int> numberList = new List<int>();//Can hold multiple integers
List<Car> carList = new List<Car>();//Can hold multple car objects
public class Car
{
    public int VinNumber { get; set; }
    public string Color { get; set; }
    public int DoorNumber { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
}

As you can see that the list can hold any type but also once a list is assigned a type then only that type can be added the to the list. The won't even compile if it's not the right type.

See More Create List Initialization Examples

How To Initialize A List

Create List With Initial Capacity

Sometimes if with working with huge amounts of data you may want to set an initial size of the list. We can do this by passing an int value as a parameter to the list. Since the list's underlying data structure is an array. It will create an array that is set to the initial capacity. Otherwise what would happen if you didn't' set it and you had a huge amount of data is that the array would be the default starting size and as the data in the list grows the default sized array would fill up and then grow as the list grew so that the array would not fill up. This operation of increasing the underlying array as item are added adds a slight performance hit. See below for an example.

Create List Default Capacity Code Example

In this example, we do a test where we add 100 million integers to a list at the default capacity meaning that we didn't set the capacity. We do that test 10 times to get the average of that 100 million test.

using System.Diagnostics;
int numberOfTests = 10;//Number of tests 
List<double> testSpeedList = new List<double>();
for (int i = 0; i < numberOfTests; i++)
{
    testSpeedList.Add(DefaultCapacitySpeedTest());
}
Console.WriteLine($"Default List Average speed:{Math.Round(testSpeedList.Average())}ms, In {numberOfTests} tests");
double DefaultCapacitySpeedTest()
{
    int numberOfFunctionCalls = 100000000;//Number of function calls made
    List<int> numberList = new List<int>();//Can hold multiple integers
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();//Start the Stopwatch timer
    for (int i = 0; i < numberOfFunctionCalls; i++)
    {
        numberList.Add(i);
    }
    stopwatch.Stop();//Stop the Stopwatch timer
    Console.WriteLine($"Default List Count:{numberList.Count}, Function calls:{numberOfFunctionCalls}, In {stopwatch.Elapsed.Minutes}m {stopwatch.Elapsed.Seconds}s {stopwatch.Elapsed.Milliseconds}ms");
    return stopwatch.Elapsed.TotalMilliseconds;
}
Code Output
Default List Count:100000000, Function calls:100000000, In 0m 0s 730ms
Default List Count:100000000, Function calls:100000000, In 0m 0s 589ms
Default List Count:100000000, Function calls:100000000, In 0m 0s 538ms
Default List Count:100000000, Function calls:100000000, In 0m 0s 571ms
Default List Count:100000000, Function calls:100000000, In 0m 0s 583ms
Default List Count:100000000, Function calls:100000000, In 0m 0s 560ms
Default List Count:100000000, Function calls:100000000, In 0m 0s 584ms
Default List Count:100000000, Function calls:100000000, In 0m 0s 552ms
Default List Count:100000000, Function calls:100000000, In 0m 0s 598ms
Default List Count:100000000, Function calls:100000000, In 0m 0s 545ms
Default List Average speed:586ms, In 10 tests

This will be our baseline for the next test but we would expect that setting the initial capacity would be faster.

Create List Initialize Capacity Code Example

In this example, we'll do the same kind of test as before but we'll set the capacity of the list. Let's see the code and results.

using System.Diagnostics;
int numberOfTests = 10;//Number of tests 
List<double> testSpeedList = new List<double>();
for (int i = 0; i < numberOfTests; i++)
{
    testSpeedList.Add(CapacitySpeedTest());
}
Console.WriteLine($"Capacity List Average speed:{Math.Round(testSpeedList.Average())}ms, In {numberOfTests} tests");
double CapacitySpeedTest()
{
    int numberOfFunctionCalls = 100000000;//Number of function calls made
    List<int> numberList = new List<int>(numberOfFunctionCalls);//Can hold multiple integers
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();//Start the Stopwatch timer
    for (int i = 0; i < numberOfFunctionCalls; i++)
    {
        numberList.Add(i);
    }
    stopwatch.Stop();//Stop the Stopwatch timer
    Console.WriteLine($"Capacity List Count:{numberList.Count}, Function calls:{numberOfFunctionCalls}, In {stopwatch.Elapsed.Minutes}m {stopwatch.Elapsed.Seconds}s {stopwatch.Elapsed.Milliseconds}ms");
    return stopwatch.Elapsed.TotalMilliseconds;
}
Code Output
Capacity List Count:100000000, Function calls:100000000, In 0m 0s 384ms
Capacity List Count:100000000, Function calls:100000000, In 0m 0s 290ms
Capacity List Count:100000000, Function calls:100000000, In 0m 0s 311ms
Capacity List Count:100000000, Function calls:100000000, In 0m 0s 317ms
Capacity List Count:100000000, Function calls:100000000, In 0m 0s 296ms
Capacity List Count:100000000, Function calls:100000000, In 0m 0s 316ms
Capacity List Count:100000000, Function calls:100000000, In 0m 0s 323ms
Capacity List Count:100000000, Function calls:100000000, In 0m 0s 292ms
Capacity List Count:100000000, Function calls:100000000, In 0m 0s 313ms
Capacity List Count:100000000, Function calls:100000000, In 0m 0s 319ms
Capacity List Average speed:317ms, In 10 tests

As we see from the setting the capacity that it is faster if we known ahead of time the count of the items we intend to add to the list then this would be a faster approach.

Create List With From Another List

Another useful feature of the list constructor is to pass another list to create a copy of a list. If we have a list that starts at 10 and counts down to t 1 and then copy that list we should see that the newest list has the same values. See example below.

List<int> numberList = new List<int>() { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };//Starting list
List<int> newNumberList = new List<int>(numberList);//A copy of the first list
foreach (int value in newNumberList)//loop through each of the values
{
    Console.WriteLine("Value:" + value);//Print the value to the console
}
Code Output
Value:10
Value:9
Value:8
Value:7
Value:6
Value:5
Value:4
Value:3
Value:2
Value:1

The newly created list has the same values as the first list and in the same order.

Add Objects To A List

The next most important function for list is adding an element. This is achieved using the Add function. You can only add a matching type of the list so you can't add a string. You can keep adding elements as long as you don't run out of memory. The list will hold everything. Also, keep in mind that duplicate values are allowed to be added. List will not require unique values which is a direct contrast to a dictionary. See example below.

Add Objects To A List Example
List<string> makeNames = new List<string>();//Create the list
makeNames.Add("SuperT48");//Add SuperT48 name to the list
makeNames.Add("TinyT34");//Add TinyT34 name to the list
makeNames.Add("BigH483");//Add BigH483 name to the list
makeNames.Add("HoldProject81");//Add HoldProject81 name to the list
makeNames.Add("FiveWindowType73");//Add FiveWindowType73 name to the list
foreach (string make in makeNames)//loop through all the names
{
    Console.WriteLine("Make:" + make);//Print the value to the console
}
Code Output
Make:SuperT48
Make:TinyT34
Make:BigH483
Make:HoldProject81
Make:FiveWindowType73

Adding in this way works dynamically and not just with hard coded values in this example. You can pass variables as well.

Add User Defined Classes To A List Example

Another way we can add to list is with a user defined class rather than just a simple value type. If we create a class called Car and initialize each one then add it to the list it looks like below.

List<Car> carList = new List<Car>();//Can hold multple car objects
carList.Add(new Car(383958943, "blue", 4, "SuperT48", "Type4", 2014));//Add a new Car to the list
carList.Add(new Car(435435123, "white", 4, "TinyT34", "Model Dx", 2017));//Add a new Car to the list
carList.Add(new Car(934904823, "red", 4, "BigH483", "SUV", 2022));//Add a new Car to the list
carList.Add(new Car(950348549, "gray", 4, "HoldProject81", "Truck", 2020));//Add a new Car to the list
carList.Add(new Car(309483049, "black", 4, "FiveWindowType73", "Compact", 2019));//Add a new Car to the list
foreach (Car car in carList)//loop through each of the values
{
    Console.WriteLine($"VinNumber:{car.VinNumber}, Color:{car.Color}, DoorNumber:{car.DoorNumber}, Make:{car.Make}, Model:{car.Model}, Year:{car.Year}");//Print the value to the console
}
public class Car
{
    public Car(int vinNumber, string color, int doorNumber, string make, string model, int year)
    {
        VinNumber = vinNumber;
        Color = color;
        DoorNumber = doorNumber;
        Make = make;
        Model = model;
        Year = year;
    }
    public int VinNumber { get; set; }
    public string Color { get; set; }
    public int DoorNumber { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
    public int Year { get; set; }
}
Code Output
    VinNumber:383958943, Color:blue, DoorNumber:4, Make:SuperT48, Model:Type4, Year:2014
    VinNumber:435435123, Color:white, DoorNumber:4, Make:TinyT34, Model:Model Dx, Year:2017
    VinNumber:934904823, Color:red, DoorNumber:4, Make:BigH483, Model:SUV, Year:2022
    VinNumber:950348549, Color:gray, DoorNumber:4, Make:HoldProject81, Model:Truck, Year:2020
    VinNumber:309483049, Color:black, DoorNumber:4, Make:FiveWindowType73, Model:Compact, Year:2019

This give the list a great flexibility in taking any system or user defined object.

Insert Objects To A List

Insert allows to add an object a a specific index of the list. So you can insert an item in the middle of the list and the list will shift down the rest of the items. For example, if there's a list of 10 items and index range is from 0-9. and we insert an item at index 5. The current item that was at index 5 will be moved to index 6 and the item was at index 6 moved to 7 and so on. The index range will update to be from 0-10. See example below.

Starting List

Index0123456789
Itemabcdefghij

List after inserting t at index 5

Index012345678910
Itemabcdeftghij

Another useful way of using insert is for ordering a list. We can use insertion sort to quickly show how insert works and how it can be used. See example code below.

Insert Objects To A List Example
List<int> unorderList = new List<int>();//Create the list
unorderList.Add(3);//Add number to the list
unorderList.Add(6);//Add number to the list
unorderList.Add(29);//Add number to the list
unorderList.Add(221);//Add number to the list
unorderList.Add(28);//Add number to the list
unorderList.Add(33);//Add number to the list
unorderList.Add(310);//Add number to the list
unorderList.Add(30);//Add number to the list
unorderList.Add(1);//Add number to the list
List<int> orderList = new List<int>();//Create the list
foreach (int number in unorderList)//Start looping through the unordered list
{
    if (orderList.Count == 0)//If order list is empty then add the first item to it
    {
        orderList.Add(number);//Add first item to the list
    }
    else
    {
        bool isAdded = false;
        for (int i = 0; i < orderList.Count; i++)//Loop through the order list
        {
            if (number < orderList[i])//If a number is less than the current item in order list then add it right before
            {
                orderList.Insert(i, number);//Insert the item before the current number
                isAdded = true;//set add flag for later
                break;//End looping through the order list, not then this will be an infinite loop
            }
        }
        if (!isAdded)//If the number was not less than any in the list then add to the end.
        {
            orderList.Add(number);//Add number to the list
        }
    }
}
foreach (int number in orderList)//Loop through the list
{
    Console.WriteLine("Number:" + number);//Print the value to the console
}
Code Output
  Number:1
  Number:3
  Number:6
  Number:28
  Number:29
  Number:30
  Number:33
  Number:221
  Number:310

Insert is great way to reorder the list or put item at specific index in the list. Be careful not try to insert to index that does not exist other way an error will be thrown.

Remove Objects From A List

There may be cases where there is unwanted values or objects in our list and need way to remove. For example, say we want to remove 4 car makes from the list SuperT48, Tesla, BigH483 and Unknown from the list. Behind the scense the list will find those values and remove them from the list. But notice that if we try to remove the car make Unknown from the list as well. It is not in the list and won't be found. This is ok as it won't cause an error.

Remove Objects From A List Example
List<string> makeNames = new List<string>();//Create the list
makeNames.Add("SuperT48");//Add SuperT48 name to the list
makeNames.Add("TinyT34");//Add TinyT34 name to the list
makeNames.Add("BigH483");//Add BigH483 name to the list
makeNames.Add("HoldProject81");//Add HoldProject81 name to the list
makeNames.Add("FiveWindowType73");//Add FiveWindowType73 name to the list
makeNames.Remove("SuperT48");//Remove the string SuperT48 from the list
makeNames.Remove("TinyT34");//Remove the string TinyT34 from the list
makeNames.Remove("BigH483");//Remove the string BigH483 from the list
makeNames.Remove("Unknown");//Try to remove Unknown from the list but doesn't exist in the list so it can't be removed and won't cause an error
foreach (string make in makeNames)//loop through all the names
{
    Console.WriteLine("Make:" + make);//Print the value to the console
}
Code Output
Make:HoldProject81
Make:FiveWindowType73

From the output, we can see that only two cars are left which is correct.

Access Certain Objects In A List

Another useful feature of a list is accessing certain elements through the indexer. This the same as accessing an array. The address of an item is number in the order that it is added, but the index starts at 0 for item1 and 1 for item 2 and so on. So care needs to be done so that the index accessed is in the array otherwise an exception is thrown. See below an example.

Access Certain Objects In A List Example
List<string> makeNames = new List<string>();//Create the list
makeNames.Add("SuperT48");//Add SuperT48 name to the list
makeNames.Add("TinyT34");//Add TinyT34 name to the list
makeNames.Add("BigH483");//Add BigH483 name to the list
makeNames.Add("HoldProject81");//Add HoldProject81 name to the list
makeNames.Add("FiveWindowType73");//Add FiveWindowType73 name to the list
Console.WriteLine("Make:" + makeNames[0]);//Print the value to the console
Console.WriteLine("Make:" + makeNames[1]);//Print the value to the console
Console.WriteLine("Make:" + makeNames[2]);//Print the value to the console
Console.WriteLine("Make:" + makeNames[3]);//Print the value to the console
Console.WriteLine("Make:" + makeNames[4]);//Print the value to the console
Code Output
Make:SuperT48
Make:TinyT34
Make:BigH483
Make:HoldProject81
Make:FiveWindowType73

Retrieving the items in this way is a powerful and quick way to get the items if you know where they are located in the list.

Additional Sort Examples

Access Items From A List By Index

Add A List To Another List

The quick way to add a list of the same type to another list is to the AddRange function. The other alternative to loop through the list and add each element to the other list. This undesirable to write each time so we use the build in function AddRange. An example is below.

Add A List To Another List Example
List<string> makeNames = new List<string>();//Create the list
makeNames.Add("SuperT48");//Add SuperT48 name to the list
makeNames.Add("TinyT34");//Add TinyT34 name to the list
makeNames.Add("BigH483");//Add BigH483 name to the list
List<string> makeNames2 = new List<string>();//Create the list
makeNames2.Add("HoldProject81");//Add HoldProject81 name to the list
makeNames2.Add("FiveWindowType73");//Add FiveWindowType73 name to the list
makeNames.AddRange(makeNames2);//add list2 to list1
foreach (string makeName in makeNames)
{
    Console.WriteLine("Make:" + makeName);//Print the value to the console
}
Code Output
Make:SuperT48
Make:TinyT34
Make:BigH483
Make:HoldProject81
Make:FiveWindowType73

AddRange works well and is efficient. It is good to know what the build in functions are available to be productive.

Sort A List

List comes with build in sorting method. The default sort does smallest to largest or A to Z but to switch that you'll need to do some create a function that will compare it to the other way. Some examples below.

Sort A List Example Code
List<int> unorderList = new List<int>();//Create the list
unorderList.Add(3);//Add number to the list
unorderList.Add(6);//Add number to the list
unorderList.Add(29);//Add number to the list
unorderList.Add(221);//Add number to the list
unorderList.Add(28);//Add number to the list
unorderList.Add(33);//Add number to the list
unorderList.Add(310);//Add number to the list
unorderList.Add(30);//Add number to the list
unorderList.Add(1);//Add number to the list
unorderList.Sort();//Sort by smalltest to Largest
Console.WriteLine("Sort from smallest to largest");//Print the value to the console
foreach (int number in unorderList)//Loop through the list
{
    Console.WriteLine("Number:" + number);//Print the value to the console
}
Console.WriteLine("");//Print the value to the console
Console.WriteLine("Sort from largest to smallest");//Print the value to the console
unorderList.Sort(CompareToOrderByDescending);//Sort by largest to smallest
foreach (int number in unorderList)//Loop through the list
{
    Console.WriteLine("Number:" + number);//Print the value to the console
}
int CompareToOrderByDescending(int a, int b)
{
    if (a > b)//Higher numbers to be beginning
    {
        return -1;
    }
    else if (a < b)//Higher numbers to be toward the end
    {
        return 1;
    }
    else
    {
        return 0;//When they are equal return 0
    }
}
Code Output
Sort from smallest to largest
Number:1
Number:3
Number:6
Number:28
Number:29
Number:30
Number:33
Number:221
Number:310

Sort from largest to smallest
Number:310
Number:221
Number:33
Number:30
Number:29
Number:28
Number:6
Number:3
Number:1

The default sort provides ascending order automatically but for descending ordering a function has to written for the custom compare.

Other Resources

Get Latest Updates
Comments Section