ZetCode

C# List

last modified January 15, 2024

In this article we show how to work with a List collection in C#.

C# List

C# List represents a strongly typed list of objects that can be accessed by index. It provides methods to search, sort, and manipulate lists.

C# List initializer

C# lists can be initialized with literal notation. The elements are added on the right side of the assignment inside {} brackets.

Program.cs
var words = new List<string> { "forest", "oak", "river", "falcon" };
Console.WriteLine(string.Join(", ", words));

In the example, we create a list of strings with list initializer.

var words = new List<string>{"forest", "oak", "river", "falcon"};

A new list is created. Between the angle brackets (<>), we specify the data type of the list elements.

Console.WriteLine(string.Join(", ", words));

To get a quick look at the contents of the list, we join all the values into a string, separated by comma.

$ dotnet run
forest, oak, river, falcon

C# List collection expression

.NET 8 introduced a terse syntax to create and initialize a list.

Program.cs
List<string> words = ["forest", "oak", "river", "falcon"];
Console.WriteLine(string.Join(", ", words));

The elements of the list are placed inside a pair of square brackets ([]). This syntax is shared by many programming languages.

C# List count elements

With the Count property, we get the number of elements in the list.

Program.cs
List<int> vals = [0, 1, 2, 3, 4, 5];
Console.WriteLine($"There are {vals.Count} elements in the list");

The example counts the number of elements in the list.

Console.WriteLine($"There are {vals.Count} elements in the list");

Here we print the number of elements in the List.

$ dotnet run
There are 6 elements in the list

C# List access elements

Elements of a list can be accessed using the index notation []. The index is zero-based.

Program.cs
List<int> vals = [0, 1, 2, 3, 4, 5];

Console.WriteLine(vals[0]);
Console.WriteLine(vals[1]);

Console.WriteLine(vals.Count - 1);

Console.WriteLine(vals[^1]);
Console.WriteLine(vals[^2]);

The example prints the first, second, and the last element of the list.

Console.WriteLine(vals.Count - 1);

To get the last element of the list, we count the number of elements and divide one.

Console.WriteLine(vals[^1]);
Console.WriteLine(vals[^2]);

With the ^ operator, we can access elements from the end of the list.

$ dotnet run
0
1
5
5
4

C# List add elements

The Add method adds an element at the end of the list. The AddRange methods adds the elements of the specified collection to the end of the list.

Program.cs
List<string> words = ["forest", "oak", "river", "falcon"];

words.Add("sky");
words.Add("ocean");

string[] words2 = ["owl", "hawk"];
words.AddRange(words2);

Console.WriteLine(string.Join(',', words));

The example creates two lists and adds new elements to it.

words.Add("sky");
words.Add("ocean");

Two elements are added to the list with Add.

string[] words2 = ["owl", "hawk"];
words.AddRange(words2);

With AddRange method, we add another collection to the list.

$ dotnet run
forest,oak,river,falcon,sky,ocean,owl,hawk

C# List insert elements

The Insert method inserts an element into the list at the specified index. The InsertRange inserts the elements of a collection into the list at the specified index.

Program.cs
List<string> words = ["forest", "oak", "river", "falcon"];

words.Insert(0, "sky");
words.Insert(words.Count, "cloud");

List<string> words2 =["water", "wave"];
words.InsertRange(2, words2);

Console.WriteLine(string.Join(", ", words));

The example creates a new list of words and inserts new elements to it.

$ dotnet run
sky, forest, water, wave, oak, river, falcon, cloud

C# List Contains

The Contains method determines whether the element is in the list.

Program.cs
List<string> words =  ["forest", "oak", "river", "falcon"];

if (words.Contains("oak"))
{
    Console.WriteLine("The list contains the oak word");
}

In the example, we check if the oak word is in the list.

C# List removing elements

The Remove, RemoveAt, RemoveAll, RemoveRange, and Clear methods can be used to remove elements from a list.

Program.cs
List<int> nums = [0, 1, 2, -3, 4, -5, 6, 7, -8, 9, 10];

nums.RemoveAll(e => e < 0);
Console.WriteLine(string.Join(", ", nums));

nums.Remove(0);
nums.RemoveAt(nums.Count - 1);

Console.WriteLine(string.Join(", ", nums));

nums.RemoveRange(0, 3);

Console.WriteLine(string.Join(", ", nums));

nums.Clear();
Console.WriteLine("{0}", nums.Count);

In the example, we remove elements from the list of integers.

nums.RemoveAll(e => e < 0);
Console.WriteLine(string.Join(", ", nums));

With RemoveAll method, we remove all elements that satisfy the given predicate; in our case, we remove all negative values.

nums.Remove(0);

The Remove method removes the first occurrence of the 0 value.

nums.RemoveAt(nums.Count - 1);

With RemoveAt, we remove the element at the given index.

nums.RemoveRange(0, 3);

The RemoveRange method removes the given range of values from the list. The parameters are the zero-based starting index of the range of elements to remove and the number of elements to remove.

C# List ToArray

The ToArray method copies the elements of a list into an array.

Program.cs
List<int> nums = [1, 2, 3, 4];

Console.WriteLine(nums.GetType());

var nums2 = nums.ToArray();
Console.WriteLine(nums2.GetType());

In the example, we create an array from a list. We print the type of both containers with GetType.

$ dotnet run
System.Collections.Generic.List`1[System.Int32]
System.Int32[]

C# List ForEach

The ForEach method performs the specified action on each element of a list.

Program.cs
List<string> words = ["forest", "oak", "river", "falcon"];

words.ForEach(Console.WriteLine);
words.ForEach(word => Console.WriteLine(word.ToUpper()));

We call the ForEach method twice on a list of words.

words.ForEach(Console.WriteLine);

We print all elements of the list.

words.ForEach(word => Console.WriteLine(word.ToUpper()));

Here we also print all elements of the list; the words are transformed to uppercase.

$ dotnet run
forest
oak
river
falcon
FOREST
OAK
RIVER
FALCON

C# loop List

There are several ways to loop over a C# list.

Program.cs
List<int> nums = [1, 2, 3, 4];

nums.ForEach(e => Console.WriteLine(e));

Console.WriteLine("********************");

foreach (int e in nums)
{

    Console.WriteLine(e);
}

Console.WriteLine("********************");

for (int i = 0; i < nums.Count; i++)
{

    Console.WriteLine(nums[i]);
}

The example shows three ways of looping over a list in C#.

nums.ForEach(e => Console.WriteLine(e));

We loop over a list with ForEach.

foreach(int e in nums)
{
    Console.WriteLine(e);
}

In this case, we use the foreach statement.

for (int i = 0; i < nums.Count; i++)
{
    Console.WriteLine(nums[i]);
}

Finally, this is the classic for loop.

$ dotnet run
1
2
3
4
********************
1
2
3
4
********************
1
2
3
4

C# sort, reverse List

The Sort method sorts the elements of a list, the Reverse method reverses the elements of the list.

Program.cs
List<int> nums = [2, 1, 5, 4, 3];

nums.Reverse();
Console.WriteLine(string.Join(',', nums));

nums.Sort();
Console.WriteLine(string.Join(',', nums));

The example reverses the elements of the list of integers and then sorts them.

$ dotnet run
3,4,5,1,2
1,2,3,4,5

C# List FindAll

The FindAll method retrieves all the elements of a list that match the conditions defined by the specified predicate. It returns a list containing all the elements that match the conditions defined by the specified predicate, if found; otherwise, an empty list.

A predicate is a method that returns a boolean value. To learn more about predicates, visit the C# Predicate tutorial.

Program.cs
List<int> vals = [-1, -3, 0, 1, 3, 2, 9, -4];
List<int> filtered = vals.FindAll(e => e > 0);

Console.WriteLine(string.Join(',', filtered));

The example finds all positive elements.

List<int> filtered = vals.FindAll(e => e > 0);

The predicate is an expression that returns true for values greater than zero.

$ dotnet run
1,3,2,9

These are the positive values of the list.

C# List Find, FindLast, FindIndex, FindLastIndex

The Find method returns the first occurrence of the element that matches the given predicate. The FindLast method returns the last occurrence of the element that matches the given predicate.

The FindIndex method returns the index of the first occurrence of the element that matches the given predicate. The FindLastIndex method returns the index of the last occurrence of the element that matches the given predicate.

Program.cs
List<int> nums = [6, -2, 1, 5, 4, 3, 2, 9, -1, 7];

var found = nums.Find(e => e < 0);
Console.WriteLine(found);

var found2 = nums.FindIndex(e => e < 0);
Console.WriteLine(found2);

var found3 = nums.FindLast(e => e < 0);
Console.WriteLine(found3);

var found4 = nums.FindLastIndex(e => e < 0);
Console.WriteLine(found4);

The example uses all the mentioned methods to find elements and indexes.

$ dotnet run
-2
1
-1
8

C# List ConvertAll

The ConvertAll method converts the elements in the current List to another type, and returns a list containing the converted elements.

Program.cs
List<string> words = ["falcon", "owl", "sky", "hawk", "stork"];

List<string> uppedWords = words.ConvertAll(s => s.ToUpper());
List<int> lengths = words.ConvertAll(s => s.Length);

Console.WriteLine(string.Join(", ", uppedWords));
Console.WriteLine(string.Join(", ", lengths));

In the example, we have a list of words. We convert the list to two other lists.

List<string> uppedWords = words.ConvertAll(s => s.ToUpper());

Here we convert the list to a list containing words transformed into uppercase.

List<int> lengths = words.ConvertAll(s => s.Length);

In the second case, the converted list contains integers that are the length of the words in the original list.

$ dotnet run
FALCON, OWL, SKY, HAWK, STORK
6, 3, 3, 4, 5

In the second example, we have a separate squareRoot method, which is applied on the list of integers.

Program.cs
double squareRoot(int x)
{
    return Math.Sqrt(x);
}

List<int> vals = [1, 4, 9, 16, 25];

Converter<int, double> converter = squareRoot;
List<double> vals2 = vals.ConvertAll(converter);

Console.WriteLine(string.Join(", ", vals2));

In the example, we create a new list by applying square root operation on the list of integers.

$ dotnet run
1, 2, 3, 4, 5

C# List shuffle

The following example randomly rebuilds a list of integer values.

Program.cs
List<int> vals = [1, 2, 3, 4, 5, 6, 7, 8];
vals.Shuffle();

var res = string.Join(" ", vals);
Console.WriteLine(res);

static class MyExtensions
{
    private static readonly Random rng = new();

    public static void Shuffle<T>(this IList<T> vals)
    {
        int n = vals.Count;

        while (n > 1)
        {
            n--;
            int k = rng.Next(n + 1);

            (vals[n], vals[k]) = (vals[k], vals[n]);
        }
    }
}

In the example, we create a Shuffle extension method. It shuffles the elements in-place.

public static void Shuffle<T>(this IList<T> vals)
{
    int n = vals.Count;

    while (n > 1)
    {
        n--;
        int k = rng.Next(n + 1);

        (vals[n], vals[k]) = (vals[k], vals[n]);
    }
}

This is an implementation of the Fisher-Yates shuffling algorithm.

$ dotnet run
7 1 5 6 4 2 3 8
$ dotnet run
7 5 4 1 2 3 6 8
$ dotnet run
2 3 8 5 1 4 7 6
$ dotnet run
4 5 1 6 8 2 3 7

C# List TrueForAll

The TrueForAll determines whether every element in the list matches the conditions defined by the given predicate.

Program.cs
List<int> nums = [1, 2, 3, 4, 5, 6, 7, 8];

var res1 = nums.TrueForAll(e => e % 2 == 0);
Console.WriteLine(res1);

var res2 = nums.TrueForAll(e => e > 0);
Console.WriteLine(res2);

In the example, we check if the elements in the list are all even and that they are all positive.

$ dotnet run
False
True

Not all elements are even and all elements are positive.

LINQ

Language-Integrated Query (LINQ) is the name for a set of technologies based on the integration of query capabilities directly into the C# language.

Via LINQ, C# exposes plenty of methods for working with List data.

C# List aggregate calculations

LINQ has extension methods to calculate aggregate calculations, such as min, max, or sum.

Program.cs
List<int> vals = [-1, 2, 1, -3, 7, -9, 5, 9, -4, 8];

var n1 = vals.Count;
Console.WriteLine($"There are {n1} elements");

var n2 = vals.Count(e => e % 2 == 0);
Console.WriteLine($"There are {n2} even elements");

var sum = vals.Sum();
Console.WriteLine($"The sum of all values is: {sum}");

var s2 = vals.Sum(e => e > 0 ? e : 0);
Console.WriteLine($"The sum of all positive values is: {s2}");

var avg = vals.Average();
Console.WriteLine($"The average of values is: {avg}");

var max = vals.Max();
Console.WriteLine($"The maximum value is: {max}");

var min = vals.Min();
Console.WriteLine($"The minimum value is: {min}");

In the program, we use the Count, Sum, Average, Max, and Min methods.

$ dotnet run
There are 10 elements
There are 3 even elements
The sum of all values is: 15
The sum of all positive values is: 32
The average of values is: 1.5
The maximum value is: 9
The minimum value is: -9

C# List ordering

To order data, we can use the OrderBy and ThenBy extension methods. Note that the methods create a new modified list; the original list is not changed.

Program.cs
List<User> users =
[
    new ("John", "Doe", 1230),
    new ("Lucy", "Novak", 670),
    new ("Ben", "Walter", 2050),
    new ("Robin", "Brown", 2300),
    new ("Amy", "Doe", 1250),
    new ("Joe", "Draker", 1190),
    new ("Janet", "Doe", 980),
    new ("Albert", "Novak", 1930),
];

Console.WriteLine("sort ascending by last name and salary");

var sortedUsers = users.OrderBy(u => u.LastName).ThenBy(u => u.Salary);

foreach (var user in sortedUsers)
{
    Console.WriteLine(user);
}

Console.WriteLine("---------------------");

Console.WriteLine("sort descending by last name and salary");

var sortedUsers2 = users.OrderByDescending(u => u.LastName)
    .ThenByDescending(u => u.Salary);

foreach (var user in sortedUsers2)
{
    Console.WriteLine(user);
}

record User(string FirstName, string LastName, int Salary);

We have a list of users. The users are sorted first by their last names, then by their salaries.

var sortedUsers = users.OrderBy(u => u.LastName).ThenBy(u => u.Salary);

We sort the users by their last names and then by their salaries in ascending order.

var sortedUsers2 = users.OrderByDescending(u => u.LastName)
    .ThenByDescending(u => u.Salary);

Here, we sort the users by their last names and then by their salaries in descending order.

$ dotnet run
sort ascending by last name and salary
User { FirstName = Robin, LastName = Brown, Salary = 2300 }
User { FirstName = Janet, LastName = Doe, Salary = 980 }
User { FirstName = John, LastName = Doe, Salary = 1230 }
User { FirstName = Amy, LastName = Doe, Salary = 1250 }
User { FirstName = Joe, LastName = Draker, Salary = 1190 }
User { FirstName = Lucy, LastName = Novak, Salary = 670 }
User { FirstName = Albert, LastName = Novak, Salary = 1930 }
User { FirstName = Ben, LastName = Walter, Salary = 2050 }
---------------------
sort descending by last name and salary
User { FirstName = Ben, LastName = Walter, Salary = 2050 }
User { FirstName = Albert, LastName = Novak, Salary = 1930 }
User { FirstName = Lucy, LastName = Novak, Salary = 670 }
User { FirstName = Joe, LastName = Draker, Salary = 1190 }
User { FirstName = Amy, LastName = Doe, Salary = 1250 }
User { FirstName = John, LastName = Doe, Salary = 1230 }
User { FirstName = Janet, LastName = Doe, Salary = 980 }
User { FirstName = Robin, LastName = Brown, Salary = 2300 }

Source

List class - language reference

In this article we have worked with a C# List collection.

Author

My name is Jan Bodnar and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.

List all C# tutorials.