views:

102

answers:

2

Hi guys,

I've been reading a fair bit about the performance of using LINQ rather than using a for each loop and from what I understand using a LINQ query would be a little bit slower but generally worth it for convenience and expressiveness. However I am a bit confused about how much slower it is if you were to use the results of the query in a for loop.

Let's say that I have a set called 'Locations' and a set of objects called 'Items'. Each 'item' can only belong to one 'location'. I want to link items that are under the same location to each other. If I were to do this using a normal 'For Each' loop it would be something like this:

For Each it as Item in Items
   If it.Location.equals(Me.Location)
      Me.LinkedItems.Add(it)
   End If
Next

However if i was to use LINQ it would instead be this:

For Each it as Item in Items.Where(Function(i) i.Location.equals(Me.Location))
   Me.LinkedItems.Add(it)
Next

Now my question is, is the second (LINQ) option going to loop once through the entire 'Items' set to complete the query, then loop through the results to add them to the list, resulting in essentially two loops, or will it do the one loop like the first (For Each) option? If the answer is the former, I assume then that it would be silly to use LINQ in this situation.

Thanks, Dane.

P.S. Sorry about the VB.NET, it's what we are coding in here so it's in my brain :S

+7  A: 

It will do one loop - it's lazily evaluated.

However, you may be able to do better than this. What's the type of LinkedItems? If it has an appropriate AddRange method, you should be able to do:

Me.LinkedItems.AddRange(Items.Where(Function(i) i.Location.equals(Me.Location)))


More on lazy evaluation

Basically Where maintains an iterator, and only finds the next matching item when you ask for it. In C#, the implementation would be something like:

// Error handling omitted
public static IEnumerable<T> Where(this IEnumerable<T> source,
                                   Func<T, bool> predicate)
{
    foreach (T element in source)
    {
        if (predicate(element))
        {
            yield return element;
        }
    }
}

It's the use of yield return here which would make it lazily evaluated. If you're not familiar with C# iterator blocks, you might want to look at these articles which explain them in more detail.

Of course Where could have been implemented "manually" instead of using an iterator block, but the above implementation is sufficient to show the lazy evaluation.

Jon Skeet
No fair! i spotted it first! ;)
RCIX
Awesome, exactly what i was looking for thanks!
link664
+3  A: 

It will do the query once, since you are foreaching over a list of Items.Where. In your case that's a pre-filtered list of the condition you want and you should really go with the LINQ.

RCIX
+1 for being faster than Skeet.
MarkJ