ListThe 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.
Here we create two separate lists of ints. We add four prime numbers to each List. The values are stored in the order added—2, 3, 5 and then 7.
Add() four times with the number as the argument. The end Count of the list is 4.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
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."
foreach-keyword and declare a variable that is assigned to each element as we pass over it.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
for-loopA 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.
List of three elements, and print its first element to the console (using index 0).for-loop, beginning at 0. We end when we reach Count—the last index accessed is Count minus 1.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, reverseMuch 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.
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, InsertRangeFor adding many elements at once, we use the InsertRange and AddRange methods. InsertRange can insert at an index, while AddRange adds at the end.
InsertRange is the index where we want to insert new elements. The second is an IEnumerable (a string array).List of two strings, then add two strings from an array to index 1. The result List has four 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, clearTo get the number of elements, access the Count property. This is fast—just avoid the Count extension method. The List Count property is the same as the array Length property.
List, add 3 elements, and then Count them. The correct value (3) is printed.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 0Here 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.
List element type or compilation will fail.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 parameterLists 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.
List to ContainsValue300. The List can be passed as an argument—only the reference, not all elements, are copied.ContainsValue300 uses a foreach-loop, which tests to see if 300 is in a list of numbers.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 TRUEIndexOfThis 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 ExistSimilar 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 3ForEachThis 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: dogTrueForAllThis method accepts a Predicate. If the Predicate returns true for each element in the List, TrueForAll() will also return true.
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 20Join string listSometimes 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 DictionaryWe 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).
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
InsertThis 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.
string List, and then add 2 string elements to it (these are the names of dogs).Insert() on the List with an argument of 1 to insert a 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
RemoveWith 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.
Remove with an argument that does not occur in the list. This will not cause an exception.RemoveAll method.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
SortThis method orders the elements in the List. For strings, Sort() 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
ReverseWith this method no sorting occurs—instead, the original order of the List is 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
GetRangeThis 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 endIt 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
CapacityWe 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
BinarySearchThis method implements the binary search algorithm. Binary search uses guesses to find the correct element faster than linear searching.
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
EqualSequenceEqual 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
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; List<int> numbers = [0, 10, 30, 50]; // Test List contents with pattern matching. if (numbers is [0, 10]) { Console.WriteLine("Is 0, 10"); } else if (numbers is [0, 10, 30, 50]) { Console.WriteLine("Is 0, 10, 30, 50"); }Is 0, 10, 30, 50Concat listsWith Concat, a method from the System.Linq namespace, we can add one list to another. Only a single method call is required.
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
ListSuppose we want to create a List of 3 elements with an initializer. If we can use an array instead, the program will be faster.
string array of 3 elements with an array initializer. We test its length.List of strings with an initializer. The List is an additional object—performance is affected.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
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.