Working With Lists in C#

In this article we will learn about Lists in C# programming.

In today's discussion, we'll see how to implement Lists. So, what is a List? To answer this questions, Lists are like arrays. That means they are an index-based collection with some bonus features like supporting adding and removing the elements from the collection. Below, in the snippet I have an array of months containing the months until November. However, I attempted to add December to the collection and found the following error.

  1. using System.Collections;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System;  
  5.   
  6. namespace Collections  
  7. {  
  8.     internal class Array  
  9.     {  
  10.         private static void Main(string[] args)  
  11.         {  
  12.             var monthsofYear = new string[]  
  13.             {  
  14.                 "January",  
  15.                 "February",  
  16.                 "March",  
  17.                 "April",  
  18.                 "May",  
  19.                 "June",  
  20.                 "July",  
  21.                 "August",  
  22.                 "September",  
  23.                 "October",  
  24.                 "November"  
  25.                   
  26.             };  
  27.             //Nice to have this feature  
  28.             monthsofYear.Add("December");  
  29.               
  30.             foreach (var month in monthsofYear)  
  31.             {  
  32.                 Console.WriteLine(month);  
  33.             }  
  34.               
  35.             Console.ReadLine();  
  36.         }  
  37.   
  38.     }  
  39.   

 
Since arrays are a readonly collection, it can't grow or shrink. Hence, to fix this problem, Lists are introduced. Now, with a minimal change when I run it, it will produce the expected output.
  1. using System.Collections;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System;  
  5.   
  6. namespace Collections  
  7. {  
  8.     internal class Array  
  9.     {  
  10.         private static void Main(string[] args)  
  11.         {  
  12.             var monthsofYear = new List<string>  
  13.             {  
  14.                 "January",  
  15.                 "February",  
  16.                 "March",  
  17.                 "April",  
  18.                 "May",  
  19.                 "June",  
  20.                 "July",  
  21.                 "August",  
  22.                 "September",  
  23.                 "October",  
  24.                 "November"  
  25.                   
  26.             };  
  27.             //Nice to have this feature  
  28.             monthsofYear.Add("December");  
  29.               
  30.             foreach (var month in monthsofYear)  
  31.             {  
  32.                 Console.WriteLine(month);  
  33.             }  
  34.               
  35.             Console.ReadLine();  
  36.         }  
  37.   
  38.     }  
  39.   



However, one point to note here is that a List is a wrapper around array Array[T]. So, when we execute the List code, what we get is a new list reference with the modified item in it. However, when the List is created it always allocates an extra block of memory to accommodate new incoming requests to add the element to the List. Now, there are two main properties associated with List. They are:
  1. Count: which gives the total no of elements present in the list.
  2. Capacity: gives the total no of count including extra block of memory to accommodate new ones.
Let's do a quick demo around this to prove the point.
  1. using System.Collections;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System;  
  5.   
  6. namespace Collections  
  7. {  
  8.     internal class Array  
  9.     {  
  10.         private static void Main(string[] args)  
  11.         {  
  12.             var monthsofYear = new List<string>  
  13.             {  
  14.                 "January",  
  15.                 "February",  
  16.                 "March",  
  17.                 "April",  
  18.                 "May",  
  19.                 "June",  
  20.                 "July",  
  21.                 "August",  
  22.                 "September",  
  23.                 "October",  
  24.                 "November"  
  25.                   
  26.             };  
  27.   
  28.             Console.WriteLine("Count:{0} and Capacity:{1} before!",monthsofYear.Count,monthsofYear.Capacity);  
  29.             monthsofYear.Add("December");  
  30.             Console.WriteLine("Count:{0} and Capacity:{1} after!",monthsofYear.Count,monthsofYear.Capacity);  
  31.             foreach (var month in monthsofYear)  
  32.             {  
  33.                 Console.WriteLine(month);  
  34.             }  
  35.               
  36.             Console.ReadLine();  
  37.         }  
  38.   
  39.     }  
  40.   
  41. }   
 
 
So I can proceed and add up to 16 items to the same list and its capacity won't change, but let's assume that I add one more item to it as shown below, then the List's capacity will be reevaluated and then it will drop the existing internal array and create a new array with the capacity 32 as shown below. Remember, as I said, Lists are a wrapper around arrays. So, under the hood arrays are only working to hold the elements.
  1. using System.Collections;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System;  
  5.   
  6. namespace Collections  
  7. {  
  8.     internal class Array  
  9.     {  
  10.         private static void Main(string[] args)  
  11.         {  
  12.             var monthsofYear = new List<string>  
  13.             {  
  14.                 "January",  
  15.                 "February",  
  16.                 "March",  
  17.                 "April",  
  18.                 "May",  
  19.                 "June",  
  20.                 "July",  
  21.                 "August",  
  22.                 "September",  
  23.                 "October",  
  24.                 "November"  
  25.                   
  26.             };  
  27.   
  28.             Console.WriteLine("Count:{0} and Capacity:{1} before!",monthsofYear.Count,monthsofYear.Capacity);  
  29.             monthsofYear.Add("December");  
  30.             monthsofYear.Add("1");  
  31.             monthsofYear.Add("2");  
  32.             monthsofYear.Add("3");  
  33.             monthsofYear.Add("4");  
  34.             monthsofYear.Add("5");  
  35.             Console.WriteLine("Count:{0} and Capacity:{1} after!",monthsofYear.Count,monthsofYear.Capacity);  
  36.               
  37.             foreach (var month in monthsofYear)  
  38.             {  
  39.                 Console.WriteLine(month);  
  40.             }  
  41.               
  42.             Console.ReadLine();  
  43.         }  
  44.   
  45.     }  
  46.   
  47. }  

However, this dynamic allocation of capacity could create performance issues when it comes to recreating the array and reference it when there are millions of item in the list, but if you are roughly sure about the list's capacity well in advance, then you can use the pass the capacity size as shown below in the snippet.
  1. using System.Collections;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System;  
  5.   
  6. namespace Collections  
  7. {  
  8.     internal class Array  
  9.     {  
  10.         private static void Main(string[] args)  
  11.         {  
  12.             var monthsofYear = new List<string>(20)  
  13.             {  
  14.                 "January",  
  15.                 "February",  
  16.                 "March",  
  17.                 "April",  
  18.                 "May",  
  19.                 "June",  
  20.                 "July",  
  21.                 "August",  
  22.                 "September",  
  23.                 "October",  
  24.                 "November"  
  25.                   
  26.             };  
  27.   
  28.             Console.WriteLine("Count:{0} and Capacity:{1} before!",monthsofYear.Count,monthsofYear.Capacity);  
  29.             monthsofYear.Add("December");  
  30.             monthsofYear.Add("1");  
  31.             monthsofYear.Add("2");  
  32.             monthsofYear.Add("3");  
  33.             monthsofYear.Add("4");  
  34.             monthsofYear.Add("5");  
  35.             Console.WriteLine("Count:{0} and Capacity:{1} after!",monthsofYear.Count,monthsofYear.Capacity);  
  36.               
  37.             foreach (var month in monthsofYear)  
  38.             {  
  39.                 Console.WriteLine(month);  
  40.             }  
  41.               
  42.             Console.ReadLine();  
  43.         }  
  44.   
  45.     }  
  46.   


 
So, here I passed 20 as the capacity, hence it produced the capacity 20 before and after it. Now, let's talk about the remove operation. Below in the screen shot, Intellisense has listed all the remove options available for the list.
 
You can use either of the following mentioned options to remove the item. Both of these operations will do the same thing but the later one will be expensive, since it needs to scan all the elements of the list until it finds the required element. However, RemoveAt will simply jump on that index and remove the element. Even after removing the element, the List will reorganize internally, so if the removal is the last element of the list, then it will be the fastest operation and if the removal of the element is the top element, then it will be the slowest operation.
  1. //Index based  
  2. monthsofYear.RemoveAt(11);  
  3. //Value based  
  4. monthsofYear.Remove("December");  
  5.             
One more thing here is that you can extend it to the readonly collection. Now, let's assume that you want to pass your collection to some other code whom you don't want this flexibility to modify your List. So, in order to do that you can use the List with readonly collection as shown below.
  1. using System.Collections;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System;  
  5.   
  6. namespace Collections  
  7. {  
  8.     internal class Array  
  9.     {  
  10.         private static void Main(string[] args)  
  11.         {  
  12.             var monthsofYear = new List<string>(20)  
  13.             {  
  14.                 "January",  
  15.                 "February",  
  16.                 "March",  
  17.                 "April",  
  18.                 "May",  
  19.                 "June",  
  20.                 "July",  
  21.                 "August",  
  22.                 "September",  
  23.                 "October",  
  24.                 "November"  
  25.                   
  26.             };  
  27.               
  28.             monthsofYear.Add("December");  
  29.   
  30.             var lstCopy = monthsofYear.AsReadOnly();  
  31.   
  32.             callingMonths(lstCopy);  
  33.              
  34.             foreach (var month in monthsofYear)  
  35.             {  
  36.                 Console.WriteLine(month);  
  37.             }  
  38.               
  39.             Console.ReadLine();  
  40.         }  
  41.   
  42.         static void callingMonths(IList<string> lstList )  
  43.         {  
  44.             lstList.RemoveAt(5);  
  45.         }  
  46.   
  47.     }  
  48.   
  49. }  
Now, if I execute it then it will throw the following exception. One more point to note here is that I am using IList here. That means I can get or set the value based on index on the collection being passed.
 
One point of caution here, even this code can be modified using reflection. To circumvent that we can modify the code a bit as shown below.
  1. //To avoid the collection from get modified at reflection level  
  2. var lstCopy= new ReadOnlyCollection<string>(monthsofYear);  
Now, let's look at another collection and that is Collection<T>. This provides an implementation of IList<T>. In fact, this is very similar to a readonly collection, the only change is that you can modify the collection here. However, there is already a good implementation of IList<T>, so why is Collection<T> required? To answer this question, List<T> is very efficient but it can't be customized. However, Collection<T> is specifically designed to be derived from, hence it's highly customizable. Below, in the screen shot, you can see various virtual methods available to be overridden.

So, here I can proceed and implement any of these and test for specific checks as shown below.


  1. class collectionClass:Collection<string>  
  2.     {  
  3.         protected override void InsertItem(int index, string item)  
  4.         {  
  5.             if(string.IsNullOrEmpty(item))  
  6.                 throw new Exception("Passed element is not valid");  
  7.             base.InsertItem(index, item);     
  8.         }  
  9.     }  
Now, let's look at the ObservableCollection<T>. Now, this is one of the most famous collections to track the changes. As soon as a list is modified, it will send the notification.
  1. using System.Collections;  
  2. using System.Collections.Generic;  
  3. using System.Collections.ObjectModel;  
  4. using System.Linq;  
  5. using System;  
  6.   
  7. namespace Collections  
  8. {  
  9.     internal class Array  
  10.     {  
  11.         private static void Main(string[] args)  
  12.         {  
  13.             ObservableCollection<string> monthsofYear = new ObservableCollection<string>  
  14.             {  
  15.                 "January",  
  16.                 "February",  
  17.                 "March",  
  18.                 "April",  
  19.                 "May",  
  20.                 "June",  
  21.                 "July",  
  22.                 "August",  
  23.                 "September",  
  24.                 "October",  
  25.                 "November"  
  26.                   
  27.             };  
  28.   
  29.             monthsofYear.CollectionChanged += monthsofYear_CollectionChanged;  
  30.             monthsofYear.Add("December");  
  31.   
  32.             foreach (var month in monthsofYear)  
  33.             {  
  34.                 Console.WriteLine(month);  
  35.             }  
  36.   
  37.             Console.ReadLine();  
  38.         }  
  39.   
  40.         static void monthsofYear_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)  
  41.         {  
  42.             Console.WriteLine("Collection changed with action {0} and item:{1} added to collection", e.Action, e.NewItems[0]);  
  43.         }  
  44.   
  45.          
  46.     }  
  47.   
  48. }  
Now, when I run it, it will produce the following output showing that the action Add added an item (December) to the collection.

 
 
With this I would like to end this List module here only. In the next section, we'll pick some other module and explain it. Until then stay tuned and Happy Coding.