One of the most commonly used collections in C# is a List collection. The List<T> class represents a collection of strongly typed objects, which we can access through their indexes. It has properties and methods for performing tasks such as adding, searching, removing, and finding items among others.

We can find the List<T> class in the System.Collection.Generic namespace and it’s the generic version of the ArrayList class.

To download the source code for this article, you can visit our GitHub repository.

Let’s discuss how to implement lists in C#.

How to Create a List in C#

When creating List<T> objects, we need to specify the data type for the values it will store. For our example, we are going to use a simple list that stores countries as an example: 

var countries = new List<string>();

Here, we can see that we need to specify the data type of that list when creating it (string). 

How to Add Items to a C# List

We can insert items into a list in a few different ways. To start things off, we can use the inbuilt Add() method to add items to a list inside a new ListOperations class: 

public List<string> AddElements() 
{
    var countries = new List<string>();
    countries.Add("Mexico");
    countries.Add("Mexico");
    countries.Add("Italy");
    return countries;
}

Here, we can see that we can add several countries to the countries list object. 

Besides using the Add() method, we can declare and initialize lists directly with values:

var otherCountries = new List<string>() { "Russia", "Bulgaria", "South Africa" };

We can verify that both list objects have an equal number of items:

var operations = new ListOperations();
var otherCountries = new List<string>() { "Russia", "Bulgaria", "South Africa" };

var countries = operations.AddElements();

Assert.AreEqual(countries.Count, otherCountries.Count);

Let’s assume we have multiple values we want to append to the countries list. In that case, we can take advantage of the AddRange() method to add items to that list:

var operations = new ListOperations();
var otherCountries = new string[3] { "Russia", "Bulgaria", "South Africa" };

var countries = operations.AddElements();
countries.AddRange(otherCountries);

Assert.AreEqual(6, countries.Count);

Here, we can verify that the countries list object has six items after calling the AddRange() method and passing otherCountries array as a parameter. 

In some cases, we may want to add an element into a specific position in a list. We need to specify the index and the value while invoking the Insert() method:

countries.Insert(1, "Canada");

Here, we insert “Canada” on position 1 on the list. In some cases, we may need to insert multiple values in a collection into the list. We can take advantage of the InsertRange() method to accomplish our goal here: 

countries.InsertRange(countries.Count - 1, otherCountries);

We can see that we can insert the values in otherCountries list from the last position of the countries list. Let’s verify if the Insert() and InsertRange() methods work as intended:

var operations = new ListOperations();
var otherCountries = new List<string>() { "Russia", "Bulgaria", "South Africa" };
var countries = operations.AddElements();

countries.Insert(1, "Canada");
countries.InsertRange(countries.Count - 1, otherCountries);

Assert.AreEqual("Canada", countries[1]);
Assert.AreEqual(7, countries.Count);

Here, we can test that position one of the countries list is “Canada” and the length of the list is now 7 after inserting the values of the otherCountries list.  

Accessing C# List Collection Items

We can read elements in a list in several ways such as indexes, for/foreach loops, and LINQ. We are going to discuss each technique in detail. 

If we know the element index, we can access it easily. Let’s say we want to retrieve “South Africa” from the list of countries using its index:

var otherCountries = new List<string>() { "Russia", "Bulgaria", "South Africa" };
var result = otherCountries[2];

Here, “South Africa” is stored in position 2 as the index in List<T> starts from 0 just like in arrays. However, in some cases, we don’t know the position of the elements we want to search, we can use for/foreach loops to access list items:

foreach (var item in otherCountries) 
{
    if (item.Equals("South Africa"))
        Assert.IsTrue(item.Equals("South Africa"));
}

Here, we can loop through the list while checking whether we can find South Africa in the list or not. This is not an ideal way of doing it, but the most basic one.

Another technique that we can use is LINQ to run queries to retrieve list values from List<T>

var result = from s in otherCountries
             where s.Equals("South Africa")
             select s;

It’s important to note that the value returned by LINQ is a List<T> object. Therefore, we can use the First() method to retrieve the first value of the result object, which is “South Africa”. 

Next, we are going to verify that all the methods return the same result:

var otherCountries = new List<string>() { "Russia", "Bulgaria", "South Africa" };

var result = from s in otherCountries
             where s.Equals("South Africa")
             select s;

foreach (var item in otherCountries) 
{
    if (item.Equals("South Africa"))
        Assert.IsTrue(item.Equals("South Africa"));
}

Assert.AreEqual("South Africa", otherCountries[2]);
Assert.AreEqual("South Africa", result.First());

Removing Items from a List Collection

To remove the first occurrence of a specific element, we can use the Remove() method:

otherCountries.Remove("Russia");

Here, we can verify that we remove the first occurrence of “Russia”:

var otherCountries = new List<string>() { "Russia", "Bulgaria", "South Africa", "Mexico", "USA" };

otherCountries.Remove("Russia");

Assert.AreEqual(4, otherCountries.Count);
CollectionAssert.DoesNotContain(otherCountries, "Russia");

On the other hand, suppose we want to remove a value in a specific position and we know which position it is, we can take advantage of the RemoveAt() method:

otherCountries.RemoveAt(0);

This way, we can remove the first element of the List<T> object, which we can verify here:

var otherCountries = new List<string>() { "Russia", "Bulgaria", "South Africa", "Mexico", "USA" };

otherCountries.RemoveAt(0);

Assert.AreEqual(4, otherCountries.Count);
CollectionAssert.DoesNotContain(otherCountries, "Russia");

To remove a range of elements, we can use the RemoveRange() method that takes two integers (index and count) as parameters:

otherCountries.RemoveRange(0, 2);

Here, the RemoveRange() method starts from position 0 and removes two elements from the otherCountries list. Next, we are going to verify that the RemoveRange() method works as we expect: 

var otherCountries = new List<string>() { "Russia", "Bulgaria", "South Africa", "Mexico", "USA" };

otherCountries.RemoveRange(0, 2);

Assert.AreEqual(3, otherCountries.Count);
CollectionAssert.DoesNotContain(otherCountries, "Russia");
CollectionAssert.DoesNotContain(otherCountries, "Bulgaria");

To remove all the elements from a list, we can use the Clear() method to accomplish our objective. Let’s verify that the methods remove specific elements:

var otherCountries = new List<string>() { "Russia", "Bulgaria", "South Africa", "Mexico", "USA" };

otherCountries.Clear();

Assert.AreEqual(0, otherCountries.Count);

Checking Elements in a List Collection

If we want to check whether a list contains specific elements, we can take advantage of the Contains() method. It returns true when that element exists in the list. Let’s check whether the list contains specific elements:

var otherCountries = new List<string>() { "Russia", "Bulgaria", "South Africa" };

CollectionAssert.DoesNotContain(otherCountries, "Malawi");
CollectionAssert.DoesNotContain(otherCountries, "France");
CollectionAssert.Contains(otherCountries, "Russia");
CollectionAssert.Contains(otherCountries, "South Africa");

Other C# List Collection Properties and Methods

Besides these most used methods, there are other methods that are available in the List<T> class:

MethodUsage
AsReadOnlyReturns a read-only collection for the current collection
BinarySearchAn inbuilt binary search algorithm implementation that returns the index of a search term
ConvertAllConverts list elements to another type and returns an object of type List
CopyToCopies a portion of a List or the whole object to an array object
EnsureCapacityThis method checks whether a List object has the specified capacity. The method doubles the current size until that capacity is reached
ExistsChecks whether a List object has elements that meet conditions we specify through predicates
FindUses predicate functions to return the first occurrence of an element
FindAllUses predicate functions to return all occurrences of an element
FindIndexReturns the index of the first occurrence of an element using predicate functions
FindLastReturns the last occurrence of an element using predicate functions
FindLastIndexReturns the index of the last occurrence of an element using predicate functions
ForeachIterates through List elements
GetEnumeratorReturns an enumerator that can be used to iterate through a List object
GetRangeReturns a shallow copy of a range of elements in a List object
IndexOfReturns the index of the first occurrence of an element in a List object
LastIndexOf Returns the index of the last occurrence of an element in a List object
ReverseReverses the order of the items in the list
SortSorts a List using the default comparer
ToArrayCopies the elements in a List object to an array object
TrimExcessSets the capacity of the List object to the actual size of the object
TrueForAllUses predicates to check whether all the elements in a List object meet specific conditions

And of course, there are a few properties available in the List<T> class as well:

PropertyUsage
CapacityWe use it to get or set the number of elements a List object can hold without resizing it
CountReturns the number of elements in a List object
Item[]Gets or sets the current element at a specific index

Conclusion

List collection in C# is one of the fundamental data structures we can use and it provides a lot of flexibility. We’ve covered some of the most used methods and properties that we encounter while working with lists every day.