List. The C# List is a collection that stores same-typed elements, one after another. When we add elements to a List, the class allocates enough memory to store them on its own.
When using List, we must specify a type parameter—types like int or string are commonly used. Lists are used in nearly all larger C# programs.
Version 2 This code adds all 4 numbers in a single expression—it is easier to read, and the code generated is the same.
using System;
using System.Collections.Generic;
// Version 1: create a List of ints.// ... Add 4 ints to it.
var numbers = new List<int>();
numbers.Add(2);
numbers.Add(3);
numbers.Add(5);
numbers.Add(7);
Console.WriteLine("LIST 1: " + numbers.Count);
// Version 2: create a List with an initializer.
List<int> numbers2 = [2, 3, 5, 7];
Console.WriteLine("LIST 2: " + numbers2.Count);LIST 1: 4
LIST 2: 4
Foreach-loop. This is the clearest loop when no index is needed. It automatically sets each element to a name we specify—here we use the identifier "prime."
Part 1 We use the "foreach" keyword and declare a variable that is assigned to each element as we pass over it.
Part 2 In the foreach-loop, we access the "prime" variable, which is the current element. We print each int to the screen.
using System.Collections.Generic;
List<int> list = new List<int>() { 2, 3, 7 };
// Part 1: loop through List with foreach.
foreach (int prime in list)
{
// Part 2: access each element with name.
System.Console.WriteLine("PRIME ELEMENT: {0}", prime);
}PRIME ELEMENT: 2
PRIME ELEMENT: 3
PRIME ELEMENT: 7
Indexes, for-loop. A List has elements like an array, and they are accessed by indexes starting at zero. For iteration, we can use these indexes in a for-loop.
Part 1 Here we create a list of 3 elements, and print its first element to the console (using index 0).
Part 2 We use a for-loop, beginning at 0. We end when we reach Count—the last index accessed is Count-1.
Part 3 We print each element index with Console.WriteLine. We use a string interpolation expression to format the string.
using System;
using System.Collections.Generic;
// Part 1: create List.// ... Print the first element of the List.
List<int> list = new List<int>(new int[]{ 2, 3, 7 });
Console.WriteLine($"FIRST ELEMENT: {list[0]}");
// Part 2: loop with for and access count.
for (int i = 0; i < list.Count; i++)
{
// Part 3: access element with index.
Console.WriteLine($"{i} = {list[i]}");
}FIRST ELEMENT: 2
0 = 2
1 = 3
2 = 7
For-loop, reverse. Much like an array, we can access the elements in a List in any way we like. We can loop over the List elements in reverse with a for-loop.
Start For a reverse loop, we must access the last element first, so we get the Count and subtract one from it. This is the last index.
using System;
using System.Collections.Generic;
var votes = new List<bool> { false, false, true };
// Loop through votes in reverse order.
for (int i = votes.Count - 1; i >= 0; i--)
{
Console.WriteLine("DECREMENT LIST LOOP: {0}", votes[i]);
}DECREMENT LIST LOOP: True
DECREMENT LIST LOOP: False
DECREMENT LIST LOOP: False
AddRange, InsertRange. For adding many elements at once, we use the InsertRange and AddRange methods. InsertRange can insert at an index, while AddRange adds at the end.
Tip The first argument to InsertRange is the index where we want to insert new elements. The second is an IEnumerable (a string array).
Result We create a List of 2 strings, then add 2 strings from an array to index 1. The result List has 4 strings.
using System;
using System.Collections.Generic;
// Create a list of 2 strings.
var animals = new List<string>() { "bird", "dog" };
// Insert strings from an array in position 1.
animals.InsertRange(1, new string[] { "frog", "snake" });
foreach (string value in animals)
{
Console.WriteLine("RESULT: " + value);
}RESULT: bird
RESULT: frog
RESULT: snake
RESULT: dog
Count, clear. To get the number of elements, access the Count property. This is fast—just avoid the Count extension method. Count, on the List type, is equal to Length on arrays.
Part 1 We create a List, add 3 elements, and then Count them. The correct value (3) is printed.
Part 2 Here we use the Clear method to erase all the elements in a List. The List then has zero elements.
using System;
using System.Collections.Generic;
// Part 1: build up List, and Count its elements.
List<bool> list = new List<bool>();
list.Add(true);
list.Add(false);
list.Add(true);
Console.WriteLine(list.Count);
// Part 2: call Clear() on List.
list.Clear();
Console.WriteLine(list.Count);3
0
Copy array. Here we create a List with elements from an array. We use the List constructor and pass it the array. List receives this parameter and fills its values from it.
Warning The array element type must match the List element type or compilation will fail.
Detail The List constructor can be used to copy a collection (like an IEnumerable). But for ranges, CopyTo can be used.
using System;
using System.Collections.Generic;
// Create new array with 3 elements.
int[] array = new int[] { 2, 3, 5 };
// Copy the array to a List.
List<int> copied = new List<int>(array);
// Print size of List.
Console.WriteLine("COPIED COUNT: {0}", copied.Count);COPIED COUNT: 3
List parameter. Lists do not exist just to be allocated and looped over. We want to test and examine elements to solve real-world problems in our programs.
Here We pass a List to ContainsValue300. The List can be passed as an argument—only the reference, not all elements, are copied.
Info ContainsValue300 uses a foreach-loop, which tests to see if 300 is in a list of numbers.
Result The values List contains the value 300, so our custom method returns the boolean true.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var values = new List<int>() { 200, 300, 500 };
// Pass list the method.
if (ContainsValue300(values))
{
Console.WriteLine("RETURNED TRUE");
}
}
static bool ContainsValue300(List<int> list)
{
foreach (int number in list)
{
// See if the element in the list equals 300.
if (number == 300)
{
return true;
}
}
// No return was reached, so nothing matched.
return false;
}
}RETURNED TRUE
IndexOf. This determines the element index of a certain value in the List collection. It searches for the first position (from the start) of the value.
using System;
using System.Collections.Generic;
var primes = new List<int>(new int[] { 19, 23, 29 });
int index = primes.IndexOf(23); // Exists.
Console.WriteLine(index);
index = primes.IndexOf(10); // Does not exist.
Console.WriteLine(index);1
-1
Contains, Find and Exist. Similar to IndexOf, these methods provide searching of the List. They vary in arguments accepted. With Predicates, we influence what elements match.
using System;
using System.Collections.Generic;
List<int> values = [1, 2, 3];
// The Contains method can be called with any possible value.
if (values.Contains(3))
{
Console.WriteLine("Contains 3");
}Contains 3
ForEach. This is a method. Sometimes we may not want to write a traditional foreach-loop. Here we call ForEach with a lambda that writes each element "a" to the console.
using System;
using System.Collections.Generic;
List<string> animals = ["bird", "dog"];
// Use ForEach with a lambda action.// ... Write each string to the console.
animals.ForEach(a => Console.WriteLine("ANIMAL: " + a));ANIMAL: bird
ANIMAL: dog
TrueForAll. This method accepts a Predicate. If the Predicate returns true for each element in the List, TrueForAll() will also return true.
And TrueForAll() checks the entire list—unless an element does not match (it has an early exit condition).
using System;
using System.Collections.Generic;
var numbers = new List<int> { 10, 11, 12 };
// Call TrueForAll to ensure a condition is true.
if (numbers.TrueForAll(element => element < 20))
{
Console.WriteLine("All elements less than 20");
}All elements less than 20
Join string list. Sometimes we need to turn many strings into one comma-delimited string. We can use string.Join—no trailing comma is present on the resulting string.
using System;
using System.Collections.Generic;
var colors = new List<string>() { "blue", "magenta", "orange" };
// Join strings into one CSV line.
string line = string.Join(",", colors);
Console.WriteLine(line);blue,magenta,orange
Keys in Dictionary. We use the List constructor to get a List of keys from a Dictionary. This is a simple way to iterate over Dictionary keys (or store them elsewhere).
Info The Keys property returns an enumerable collection of keys. In many programs, a List of these elements is more usable.
using System;
using System.Collections.Generic;
// Populate example Dictionary.
var dict = new Dictionary<int, bool>();
dict.Add(3, true);
dict.Add(5, false);
// Get a List of all the Keys.
List<int> keys = new List<int>(dict.Keys);
foreach (int key in keys)
{
Console.WriteLine(key);
}3, 5
Insert. This method places an element at an index. We pass in the index (an integer) and the element we wish to place in the list. Insert can cause performance problems.
Part 1 We create an empty string list, and then add 2 string elements to it (these are the names of dogs).
Part 2 We call insert to place a new dog name as the second element. Later elements ("beagle") now come after the new second element.
using System;
using System.Collections.Generic;
// Part 1: call add to populate list.
List<string> dogs = new List<string>();
dogs.Add("spaniel"); // Contains: spaniel.
dogs.Add("beagle"); // Contains: spaniel, beagle.// Part 2: insert element in second position.
dogs.Insert(1, "dalmatian");
foreach (string dog in dogs)
{
Console.WriteLine(dog);
}spaniel
dalmatian
beagle
Remove. With this method we eliminate the first matching element in the List. If we pass the value 20 to Remove, the first element in the list with value 20 is removed.
Info We can call Remove with an argument that does not occur in the list. This will not cause an exception.
using System;
using System.Collections.Generic;
var numbers = new List<int>() { 10, 20, 30 };
// Remove this element by value.
numbers.Remove(20);
foreach (int number in numbers)
{
Console.WriteLine("NOT REMOVED: {0}", number);
}
// This has no effect.
numbers.Remove(2000);NOT REMOVED: 10
NOT REMOVED: 30
Sort. This method orders the elements in the List. For strings it orders alphabetically. For integers (or other numbers) it orders from lowest to highest.
using System;
using System.Collections.Generic;
List<string> values = ["zero", "abacus", "thousand"];
// Sort in alphabetical order in-place.
values.Sort();
foreach (var value in values)
{
Console.WriteLine(value);
}abacus
thousand
zero
Reverse. With this method no sorting occurs—the original order is intact but inverted. The strings contained in the List are left unchanged.
using System;
using System.Collections.Generic;
var list = new List<string>() { "low", "high", "medium" };
// Reverse List in-place, no new variables required.
list.Reverse();
foreach (string value in list)
{
Console.WriteLine(value);
}medium
high
low
GetRange. This method returns a range of elements in a List. We specify the first and last indexes (these indexes are the same as those used to access elements). The resulting value is a new List.
using System;
using System.Collections.Generic;
var values = new List<string>() { "left", "right", "top", "bottom" };
// Get values at indexes 1 through 2.
List<string> range = values.GetRange(1, 2);
foreach (string value in range)
{
Console.WriteLine(value);
}right
top
Index from end. It is possible to access elements in a List from the last element. In this syntax, "^1" means the last element, and "^2" indicates one before the last.
using System;
using System.Collections.Generic;
var values = new List<string>() {"cat", "dog", "bird"};
// Use index from end.
var last = values[^1];
var middle = values[^2];
Console.WriteLine($"{last} {middle}");bird dog
Capacity. We can use the Capacity property on List, or pass an integer to the constructor (which sets an initial capacity) to improve allocation performance.
using System;
using System.Collections.Generic;
var list = new List<string>(100);
Console.WriteLine(list.Capacity);
// No resizes or element copies will occur, as the List has enough capacity.
for (var i = 0; i < 100; i++)
{
list.Add(i.ToString());
}100
BinarySearch. This method implements the binary search algorithm. Binary search uses guesses to find the correct element faster than linear searching.
Important To use BinarySearch correctly, we must first call Sort() on the List, or have an already-sorted list.
using System;
using System.Collections.Generic;
var list = new List<string>() { "a", "b", "c" };
// Use binary search on a list that is sorted already.
var index = list.BinarySearch("b");
Console.WriteLine(index);1
Equal. SequenceEqual is a method from System.Linq. It tells us whether 2 collections have the same exact elements. The number of elements and order must be the same.
using System;
using System.Collections.Generic;
using System.Linq;
var numbers = new List<int> { 10, 20, 30 };
var numbers2 = new List<int> { 10, 20, 30 };
// See if the two lists are equal.
if (numbers.SequenceEqual(numbers2))
{
Console.WriteLine("LISTS ARE EQUAL");
}LISTS ARE EQUAL
Is operator. How can we quickly test if a List has certain values? With the is-operator, we can test the contents of a List collection against constant data.
using System;
using System.Collections.Generic;
using System.Linq;
var left = new List<string> { "one", "two" };
var right = new List<string> { "three", "four" };
// Use Concat and ToList to merge the lists together.
var combined = left.Concat(right).ToList();
// Print the merged list.
Console.WriteLine(string.Join(",", combined));one,two,three,four
Benchmark, create List. Suppose we want to create a List of 3 elements with an initializer. If we can use an array instead, the program will be faster.
Version 1 We create a string array of 3 elements with an array initializer. We test its length.
Version 2 This code creates a List of strings with an initializer. The List is an additional object—performance is affected.
Result It is faster to allocate an array of strings—this was tested in 2023, on .NET 7 for Linux.
using System;
using System.Collections.Generic;
using System.Diagnostics;
const int _max = 1000000;
var s1 = Stopwatch.StartNew();
// Version 1: create string array with 3 elements in it.
for (int i = 0; i < _max; i++)
{
var items = new string[] { "bird", "frog", "fish" };
if (items.Length == 0)
{
return;
}
}
s1.Stop();
var s2 = Stopwatch.StartNew();
// Version 2: create string list with 3 elements in it.
for (int i = 0; i < _max; i++)
{
var items = new List<string>() { "bird", "frog", "fish" };
if (items.Count == 0)
{
return;
}
}
s2.Stop();
Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns"));
Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns"));12.57 ns Create string array
59.07 ns Create string List
Summary. The List syntax is at first confusing—but we become accustomed to it. In most programs lacking strict memory or performance constraints, this class is ideal.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Sep 25, 2024 (edit link).