A Comparison Between IEnumerable<T> and List<T> In C#

A Comparison Between IEnumerable and List Banner Image

Introduction

IEnumerable<T> and List<T> are two common C# data structures for storing data. Even when using a List<T>, the interface IEnumerable<T> is returned from LINQ functions. So this can make you wonder when should you use IEnumerable<T> or List<T> or even wonder why one is better. I'll be going through all those nuances in this article.

Method Comparison

First, I find it helpful to take a step back and look at the overall picture. There is a progression of methods between the different types and classes to get to List<T> because of the inheritance involved.

IEnumerable<T> ICollection<T>IList<T>
GetEnumeratorGetEnumeratorGetEnumerator
AddAdd
ClearClear
ContainsContains
CopyToCopyTo
RemoveRemove
IndexOf
Insert
RemoveAt

From this table with methods of each type, we can see that IEnumerable<T> only has one method. It's a function that is limited but by the time it gets to a List<T> there are a lot more methods. This is not the complete list. If you want to see the full list visit Microsoft's documentation of List<T> Class. A List<T> implements the interfaces from both IEnumerable<T> and ICollection<T> which you can see an expanded view of the extensions below.

public class List<T> : ICollection<T>, IEnumerable<T>, IEnumerable, ICollection, IEnumerable, IReadOnlyList<T>
{
    //Methods
}

A List<T> has a lot more functionality. I wrote another article that introduces the main functionality of List<T> so if you're wondering how to use these methods. Then you can go to Introduction To List. Based on the methods, we can generalize that IEnumerable<T> main task is to loop or iterate over the objects. This is also read only since there are no methods to manipulate the collection. But if you need to add, move, or remove items in the collection in addition to the loop then List would be the better option. One key to coding and programming, in general, is to keep things as simple as possible and only use what you need.

Performance Comparison

One test that we can do is to do 10 tests called a Filter then loop 100 times with each loop going through 1 million objects. I will give an average speed for the 10 tests. This will indicate how fast the methods are. Take a look at the code below.

IEnumerable<T> Performance Test

using System.Diagnostics;
int numberOfTests = 10;//Number of tests 
List<double> testSpeedList = new List<double>();

for (int i = 0; i < numberOfTests; i++)
{
    testSpeedList.Add(GetIEnumerableFilterSpeedTest());
}
Console.WriteLine($"Filter With IEnumerable Loop Average speed:{Math.Round(testSpeedList.Average())}ms, In {numberOfTests} tests");

double GetIEnumerableFilterSpeedTest()
{
    int numberOfFunctionCalls = 1000;//Number of function calls made
    Stopwatch stopwatch = new Stopwatch();
    IEnumerable<int> data = GetIEnumerableData();//Get intial random generated list
    for (int i = 0; i < numberOfFunctionCalls; i++)
    {
        stopwatch.Start();//Start the Stopwatch timer
        IEnumerable<int> filteredList = FilterWithIEnumerable(data);//Filter through IEnumerable<T>
        stopwatch.Stop();//Stop the Stopwatch timer
    }
    stopwatch.Stop();//Stop the Stopwatch timer
    Console.WriteLine($"Function calls:{numberOfFunctionCalls}, In {stopwatch.Elapsed.Minutes}m {stopwatch.Elapsed.Seconds}s {stopwatch.Elapsed.Milliseconds}ms");
    return stopwatch.Elapsed.TotalMilliseconds;
}

IEnumerable<int> FilterWithIEnumerable(IEnumerable<int> data)
{
    int count = 0;
    IEnumerable<int> filterDatas = data.Where(x => x % 2 == 0);//Filter Data on even numbers and return a IEnumerable<T>
    foreach (int filterData in filterDatas)
    {
        count++;
    }
    return filterDatas;
}

Supporting Code
IEnumerable<int> GetIEnumerableData()
{
    List<int> ints = new List<int>();

    for (int i = 0; i <= 1000000; i++)
    {
        ints.Add(GetRandomInt(0, 10000));
    }
    IEnumerable<int> converted = ints;
    return converted;
}


int GetRandomInt(int minNumber, int maxNumber)
{
    Random random = new Random();//Create Random class
    int randomInt = random.Next(minNumber, maxNumber);//Get a random number between 1 and the maxnumber
    return randomInt;
}

Code Output
Function calls:1000, In 0m 8s 453ms
Function calls:1000, In 0m 8s 434ms
Function calls:1000, In 0m 8s 434ms
Function calls:1000, In 0m 8s 440ms
Function calls:1000, In 0m 8s 389ms
Function calls:1000, In 0m 8s 397ms
Function calls:1000, In 0m 8s 436ms
Function calls:1000, In 0m 8s 398ms
Function calls:1000, In 0m 8s 402ms
Function calls:1000, In 0m 8s 396ms
Filter With IEnumerable Loop Average speed:8418ms, In 10 tests

Looping through an IEnumerable<T> yields about 4 seconds in my test. This is pretty fast considering how many looks and objects the test went through. Next, I will test List<T> to see how it performs.

List<T> Performance Test

I will perform the same test as with IEnumerable<T> and apply it to the list. Below is the resulting code.



using System.Diagnostics;
int numberOfTests = 10;//Number of tests 
List<double> testSpeedList = new List<double>();

for (int i = 0; i < numberOfTests; i++)
{
    testSpeedList.Add(GetListLoopSpeedTest());
}
Console.WriteLine($"Filter With List Average speed:{Math.Round(testSpeedList.Average())}ms, In {numberOfTests} tests");

double GetListLoopSpeedTest()
{
    int numberOfFunctionCalls = 1000;//Number of function calls made
    Stopwatch stopwatch = new Stopwatch();
    List<int> data = GetListData();//Get intial random generated list
    for (int i = 0; i < numberOfFunctionCalls; i++)
    {
        stopwatch.Start();//Start the Stopwatch timer
        FilterWithList(data);///Filter With List
        stopwatch.Stop();//Stop the Stopwatch timer
    }
    stopwatch.Stop();//Stop the Stopwatch timer
    Console.WriteLine($"Function calls:{numberOfFunctionCalls}, In {stopwatch.Elapsed.Minutes}m {stopwatch.Elapsed.Seconds}s {stopwatch.Elapsed.Milliseconds}ms");
    return stopwatch.Elapsed.TotalMilliseconds;
}

void FilterWithList(List<int> ints)
{

    List<int> filterDatas = new List<int>();
    foreach (int item in ints)
    {
        if(item % 2 == 0)
        {
            filterDatas.Add(item);
        }
    }
    int counter = 0;
    foreach (var filterData in filterDatas)
    {
        counter++;
    }
}


Supporting Code
List<int> GetListData()
{
    List<int> ints = new List<int>();

    for (int i = 0; i <= 1000000; i++)
    {
        ints.Add(GetRandomInt(0, 10000));
    }
    List<int> converted = ints;
    return converted;
}


int GetRandomInt(int minNumber, int maxNumber)
{
    Random random = new Random();//Create Random class
    int randomInt = random.Next(minNumber, maxNumber);//Get a random number between 1 and the maxnumber
    return randomInt;
}



Code Output
Function calls:1000, In 0m 10s 247ms
Function calls:1000, In 0m 10s 209ms
Function calls:1000, In 0m 10s 170ms
Function calls:1000, In 0m 10s 171ms
Function calls:1000, In 0m 10s 176ms
Function calls:1000, In 0m 10s 182ms
Function calls:1000, In 0m 10s 250ms
Function calls:1000, In 0m 10s 224ms
Function calls:1000, In 0m 10s 247ms
Function calls:1000, In 0m 10s 224ms
Filter With List Average speed:10210ms, In 10 tests

From this test, the List<T> finishes in about 10 seconds.

Which Method Is Faster IEnumerable<T> or List<T>?

From this loop test, List<T> finishes in about 10 seconds and IEnumerable<T> finishes in about 8 seconds making IEnumerable<T> faster as IEnumerable<T>.

Situational Conditions

There may be times when a method might return an IEnumerable<T>.What should you do? There are LINQ functions that return an IEnumerable<T>. Should you convert it to a list or keep it as IEnumerable<T>? It depends on what you plan to do. Let's look at each of those.

When IEnumerable<T> Has An Advantage

If you intend to just loop the items after getting an IEnumerable<T> then you shouldn't change the IEnumerable<T> to a List<T>. IEnumerable<T> is faster than List in this situation. Why waste the time doing that when you just need to loop through, it's not worth it. This is the advantage of IEnumerable<T> with the LINQ functions. You get an IEnumerable<T> from LINQ and you can just loop through the results.

List<int> data = GetListData();//Get List of Data

IEnumerable<int> filterData = data.Where(x => x % 2 == 0);//Filter Data on even numbers and return a IEnumerable&lt;T&gt;

foreach 
Console.WriteLine($"Data:{string.Join(",", filterData)}");//Print To Screen

Supporting Code
List<int> GetListData()
{
    List<int> ints = new List<int>();

    for (int i = 0; i <= 10; i++)
    {
        ints.Add(GetRandomInt(0, 10000));
    }
    List<int> converted = ints;
    return converted;
}

int GetRandomInt(int minNumber, int maxNumber)
{
    Random random = new Random();//Create Random class
    int randomInt = random.Next(minNumber, maxNumber);//Get a random number between 1 and the maxnumber
    return randomInt;
}
Code Output
Data:2322
Data:1400
Data:5708
Data:7160
Data:7960

When List<T> Has An Advantage

If you plan to change the returned list after LINQ operation or sorting. For any potential modification to the collection then you should change the IEnumerable<T> to a List<T> by using the ToList method.


List<int> data = GetListData();//Get List of Data

List<int> filterDatas = data.Where(x => x % 2 == 0).ToList();//Filter Data on even numbers and return a List

filterDatas.Sort();//Sort list

foreach (int filterData in filterDatas)
{
    Console.WriteLine($"Data:{filterData}");//Print To Screen
}
Supporting Code
List<int> GetListData()
{
    List<int> ints = new List<int>();

    for (int i = 0; i <= 10; i++)
    {
        ints.Add(GetRandomInt(0, 10000));
    }
    List<int> converted = ints;
    return converted;
}


int GetRandomInt(int minNumber, int maxNumber)
{
    Random random = new Random();//Create Random class
    int randomInt = random.Next(minNumber, maxNumber);//Get a random number between 1 and the maxnumber
    return randomInt;
}
Code Output
Data:66
Data:214
Data:5270
Data:7530
Data:7532
Data:7928

What Method Is Better IEnumerable or List?

Overall RankNameMethod CountLoop Speed
1IEnumerable18418ms
2List> 910210ms

When comparing List to IEnumerable<T>, List has more functionality but using LINQ with IEnumerable<T> it has better performance than List. But this works best when you need to filter results with LINQ and then get an IEnumerable<T> and loop through results. If you need to alter the resulting IEnumerable<T> result from LINQ then it would be best to convert the IEnumerable<T> to a List by using ToList.

Have any other insights into these collection types? Let me know in the comments below.

Get Latest Updates
Comments Section