Ienumerable VS List - What to Use - How Do They Work

IEnumerable vs List - What to Use? How do they work?

IEnumerable describes behavior, while List is an implementation of that behavior. When you use IEnumerable, you give the compiler a chance to defer work until later, possibly optimizing along the way. If you use ToList() you force the compiler to reify the results right away.

Whenever I'm "stacking" LINQ expressions, I use IEnumerable, because by only specifying the behavior I give LINQ a chance to defer evaluation and possibly optimize the program. Remember how LINQ doesn't generate the SQL to query the database until you enumerate it? Consider this:

public IEnumerable<Animals> AllSpotted()
{
return from a in Zoo.Animals
where a.coat.HasSpots == true
select a;
}

public IEnumerable<Animals> Feline(IEnumerable<Animals> sample)
{
return from a in sample
where a.race.Family == "Felidae"
select a;
}

public IEnumerable<Animals> Canine(IEnumerable<Animals> sample)
{
return from a in sample
where a.race.Family == "Canidae"
select a;
}

Now you have a method that selects an initial sample ("AllSpotted"), plus some filters. So now you can do this:

var Leopards = Feline(AllSpotted());
var Hyenas = Canine(AllSpotted());

So is it faster to use List over IEnumerable? Only if you want to prevent a query from being executed more than once. But is it better overall? Well in the above, Leopards and Hyenas get converted into single SQL queries each, and the database only returns the rows that are relevant. But if we had returned a List from AllSpotted(), then it may run slower because the database could return far more data than is actually needed, and we waste cycles doing the filtering in the client.

In a program, it may be better to defer converting your query to a list until the very end, so if I'm going to enumerate through Leopards and Hyenas more than once, I'd do this:

List<Animals> Leopards = Feline(AllSpotted()).ToList();
List<Animals> Hyenas = Canine(AllSpotted()).ToList();

Practical difference between List and IEnumerable

One important difference between IEnumerable and List (besides one being an interface and the other being a concrete class) is that IEnumerable is read-only and List is not.

So if you need the ability to make permanent changes of any kind to your collection (add & remove), you'll need List. If you just need to read, sort and/or filter your collection, IEnumerable is sufficient for that purpose.

So in your practical example, if you wanted to add the four strings one at a time, you'd need List. But if you were instantiating your collection all at once, you could use IEnumerable.

IEnumerable firstFourLettersOfAlphabet = new[]{"a","b","c","d"};

You could then use LINQ to filter or sort the list however you wanted.

IEnumerable vs List as a parameter

Why would the interface version be so much slower (and memory intensive) than the concrete version?

When it uses the interface, the iteration has to allocate an object on the heap... whereas List<T>.GetEnumerator() returns a List<T>.Enumerator, which is a struct, and doesn't require any additional allocation. List<T>.Enumerator implements IEnumerator<T>, but because the compiler knows about the concrete type directly, it doesn't need to be boxed.

So even though both methods are operating on an object of the same type (a List<T>) one calls this method:

IEnumerator<T> GetEnumerator()

... and one calls this:

List<T>.Enumerator GetEnumerator()

The first almost certainly just delegates to the second, but has to box the result because IEnumerator<T> is a reference type.

The fact that List<T>.GetEnumerator() returns a mutable struct can have some surprising consequences but it's designed precisely to have the performance benefit you're seeing here.

The use of an interface vs a concrete type can itself have some very minor performance penalties, but the primary cause here is the difference in allocation.

IEnumerable vs List in a cshtml file

IEnumerable is a more generic type than ICollection, IList and List.

If you are not using any method like "AddRange", which is just for List, or "Add", which is just for IList (and List) but you just need to loop over the collection with a foreach you should prefer IEnumerable.

This allows you to use any implementation of IEnumerable, such as List or HashSet:

public ActionResult MyAction() {
List<Product> list = GetProducts();
return View(list); //works in both cases (IEnumerable and List)
}

public ActionResult MyAction() {
HashSet<Product> list = GetProducts();
return View(list); //does not work if you declare the cshtml Model as List
}

public ActionResult MyAction() {
IEnumerable<Product> products = GetProducts();
return View(products); // works only if you declare the Model as IEnumerable
}

That said, if you declare the model as IEnumerable of Product you don't have to worry about the implementation that will be used

What's the difference between IEnumerable and Array, IList and List?

IEnumerable provides only minimal "iterable" functionality. You can traverse the sequence, but that's about it. This has disadvantages -- for example, it is very inefficient to count elements using IEnumerable, or to get the nth element -- but it has advantages too -- for example, an IEnumerable could be an endless sequence, like the sequence of primes.

Array is a fixed-size collection with random access (i.e. you can index into it).

List is a variable-size collection (i.e. you can add and remove elements) with random access.

IList is an interface which abstracts list functionality (count, add, remove, indexer access) away from the various concrete classes such as List, BindingList, ObservableCollection, etc.

Comparison between List, IList, and IEnumerable

  • IEnumerable<T> is the base interface that the following extend or implement. It doesn't allow for direct access and is readonly. So use this only if you intend to iterate over the collection.

  • ICollection<T> extendsIEnumerable<T> but in addition allows for adding, removing, testing whether an element is present in the collection and getting the total number of elements. It doesn't allow for directly accessing an element by index. That would be an O(n) operation as you need to start iterating over it until you find the corresponding element.

  • IList<T> extends ICollection<T> (and thus it inherits all its properties) but in addition allows for directly accessing elements by index. It's an O(1) operation.

  • List<T> is just a concrete implementation of the IList<T> interface.

In your code you should always expose the type that's highest in the object hierarchy that will correspond to the needs of the callers. So for example if the callers are only going to enumerate over the dataset, use IEnumerable<T>. If they need to have direct access to elements by index expose an IList<T>.

List<T> should only be used internally by your code but usually not present in the signature of the methods you are exposing. This gives you more flexibility as you could easily swap the concrete implementation without breaking the contract.

FOR-EACH over an IEnumerable vs a List

No there isn't. In both cases the for-each is translated to something like this

var enumerator = input.GetEnumerator();
while(enumerator.MoveNext())
{
// loop body.
// The current value is accessed through: enumerator.Current
}

Additionally, if the enumerator is disposable, it will be disposed after the loop.

Jon Skeet gives a detailed description here.

What should I use an IEnumerable or IList?

Generally speaking, you should try and use the least specific type that suits your purpose. IEnumerable is less specific than IList (IList implements IEnumerable) so unless you want something specific from IList (such as Count as you suggest, or perhaps Add, Delete, etc), I'd use IEnumerable.

One benefit of remaining with IEnumerable is that you can write iterator methods to return this type (look up "yield return" and iterator methods if you are not familiar with them). This allows you to write very memory efficient "pipelines" for your loops.



Related Topics



Leave a reply



Submit