4

I have a List<String> myList.

I want to take the first 10 items in this list that match some criteria (let's say .Contains("a"), for example).

I have:

Var results = myList.Where(o=>o.Contains("a")).Take(10);

Which works fine, but has LINQ performed a Where to retrieve all of the items meeting this criteria and then only took the first 10 of those? Or will this be compiled in a way that the entire LINQ statement is taken into consideration (i.e. it will perform the Where but only up until it reaches 10 items)?

  • See: [Order of LINQ extension methods does not affect performance?](http://stackoverflow.com/questions/10110013/order-of-linq-extension-methods-does-not-affect-performance) – Tim Schmelter Oct 24 '13 at 13:24
  • @Arran LINQ2Entities. I'm assuming the answer varies for those different types of LINQ then? –  Oct 24 '13 at 13:24
  • @Arran, he's already got a concrete list so linq to objects. – Rush Frisby Oct 24 '13 at 13:26
  • @rushonerok - good point, I've clearly stated this is a List in the first line of my post. Interested to see what made you ask that Arran? –  Oct 24 '13 at 13:28
  • As I have said probably a hundred times on this site: if I could teach all LINQ new users only one thing it would be that the result of a query expression is **a query**, not **the results of executing the query**. – Eric Lippert Oct 24 '13 at 14:53

3 Answers3

8

LINQ uses lazy evaluation. When you do the following line:

var results = myList.Where(o=>o.Contains("a")).Take(10);

Nothing happens. Only the query is built. When you enumerate results, (e.g. with a foreach or a ToList()) then the Where and Take will be applied to myList: Where will be executed, as needed, until up to 10 true values are found.

Tim S.
  • 55,448
  • 7
  • 96
  • 122
4

The Where statement returns an IEnumerable, which is waiting to be enumerated. The execution of the Where logic is delayed until you 'ask' the IEnumerable for its next value.

The Take(10) statement does just that - asks it for its 'next match' for the Where condition. This will execute 10 times, then finish. But of course, for the same reason, the logic of the Take(10) statement is not actually executed until you enumerate through the final returned value (results, in your case).

So yes, it's kind of optimized, but probably not quite in the way you imagined.

Baldrick
  • 11,712
  • 2
  • 31
  • 35
3

Look at the code below:

using System;
using System.Collections.Generic;
using System.Linq;

public class Test {
    public static void Main(String[] args) {
        var l = new List<String>() { "a", "ab", "b", "bb", "bc", "ba", "c", "ca", "cc" };

        foreach (var s in l.Where(e => { Console.WriteLine(e); return e.Contains("a"); }).Take(3)) {
            Console.WriteLine("loop: {0}", s);
        }
    }
}

The output is:

a
loop: a
ab
loop: ab
b
bb
bc
ba
loop: ba

So as you can see it is optimized (the strings after "c" are not evaluated).

Sebastian
  • 3,764
  • 21
  • 28