Using LINQ where (lamdaexp). First and first (lambdaexp)

Is there a difference in the two approaches? I am getting the same result, but I am trying to figure out what is correct and efficient

approaching 1:

Product product12 = products.Where(p => p.ProductID == 12).First();

      

approach 2:

Product prod12 = products.First(p => p.ProductID == 12);

      

+3


source to share


6 answers


(I am assuming you are using Linq for .Net)
First of all, let's take a look at their source codes:

Here is Where()

:

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null) 
        throw Error.ArgumentNull("source");
    if (predicate == null) 
        throw Error.ArgumentNull("predicate");
    if (source is Iterator<TSource>) 
        return ((Iterator<TSource>)source).Where(predicate);
    if (source is TSource[]) 
        return new WhereArrayIterator<TSource>((TSource[])source, predicate);
    if (source is List<TSource>) 
        return new WhereListIterator<TSource>((List<TSource>)source, predicate);
    return new WhereEnumerableIterator<TSource>(source, predicate);
}

      

And so First()

   public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
            if (source == null) throw Error.ArgumentNull("source");
            if (predicate == null) throw Error.ArgumentNull("predicate");
            foreach (TSource element in source) {
                if (predicate(element)) return element;
            }
            throw Error.NoMatch();
        }

      

And find what each code does:

  • products.First(p => p.ProductID == 12);

Based on the source code First()

, we can say that it First()

will iterate over the collection and stop iterating when it finds the first item in the collection that sets the criteria.

  1. products.Where(p => p.ProductID == 12).First();



It will first create an iterator in the method Where

where the elements meet the criteria. Then add the first element of this iterator again. And will return the first element again as soon as it finds it .

As a side note, LINQ uses deferred execution in some of its methods. And it has something to do with the outcome of your question.

Deferred Execution is a sample execution model by which the CLR ensures that a value is retrieved only when requested from an IEnumerable-based information source. When any Linq statement uses deferred execution, the CLR encapsulates related information, such as the original sequence, predicate, or selector (if any), into an iterator to be used when the information is retrieved from the original sequence using the ToList or ForEachmethod method, or manually. using basic GetEnumerator and MoveNext Methods in C #.

And, the question. Which one is faster?


The main thing is that Where().First

faster than First

. in the case of List

andArray

. Otherwise it First()

will be faster. Here is a detailed breakdown in @ Akash Kava's answer .

Pay attention to the implementation Where()

. It will return WhereListIterator()

if your collection is a List, but First()

will just iterate over the source code. And in my opinion they sped up the implementation WhereListIterator

... And after that, we call a method First()

that does not take a predicate as input and will only iterate over the filtered collection.

Also, as I understand it, the Iterator Where

avoids the indirect virtual call to the table, but it calls the iterator methods directly.
And this is the reason for this acceleration.

+7


source


I just ran a test, you can see the results here .Net Fiddle, https://dotnetfiddle.net/3lCqsR

The true answer is that which is faster depends on the type of filter applied and the type of collection that is being repeated.

    Func<Item,bool> filter = x => x.Size == 200;

    Run("First", () => items.First( filter ));
    Run("Where First", () => items.Where( filter ).First());

88 milliseconds for First
69 milliseconds for Where First


    filter = x => x.Size < 200;

    Run("First", () => items.First( filter ));
    Run("Where First", () => items.Where( filter ).First());

2 milliseconds for First
4 milliseconds for Where First


    filter = x => x.Size > 200;

    Run("First", () => items.First( filter ));
    Run("Where First", () => items.Where( filter ).First());

88 milliseconds for First
71 milliseconds for Where First

      

So there is no definite answer.

Another test, https://dotnetfiddle.net/k11nX6 , always the first one is faster than the first one.

UPDATE



After parsing the List class, you find that Where (). First () is only faster than First (), in case of List and Array only, it doesn't apply to IQueryable or any other form of Enumerable. The reason is that the enumerator used in the List is not cached per thread. List always creates a new Enumerator, so First () uses List Enumerator (iterator).

The WhereListIterator used in Where () for a List does not create a new Enumerator, instead it caches the current Iterator on the thread. This allows Where (). First () run faster.

However, this WhereListIterator caching operation is costly, so for less collection and some conditions that yield less results, Where (). First () is slower.

If you look at this example, First Beats Where First is always.

https://dotnetfiddle.net/OrUUSG

+1


source


They are functionally equivalent and equally effective. LINQ builds a query that is not evaluated until the result is listed, e.g. a foreach loop, or in this case a First () operation.

This way, both will evaluate items in their original order until they find the first instance with ProductID == 12 and return that instance if found (and throws an exception if not).

However, the latter approach is my preferred method as it is more concise and generally easier to read.

0


source


I think your question is more like:

  • Will Where

    iterate through the entire collection before switching to First

    ?
  • Will it First

    do the same?

I wrote my own implementation in Where

and First

.
This example is not an exact .net implementation, but it works in the same concept.

public static class Extensions
{
    public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> collection, Func<T,bool> condition)
    {
        foreach(var item in collection)
        {
            if(condition(item))
            {
                yield return item;
            }
        }
    }

    public static T MyFirst<T>(this IEnumerable<T> collection, Func<T,bool> condition)
    {
        foreach (var item in collection)
        {
            if (condition(item))
            {
                return item;
            }
        }

        throw new InvalidOperationException("No element found");
    }
}

      

Execute this code:

List<int> myList = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var five = myList.MyWhere(i => i < 6).MyFirst(i => i == 5);

      

Put a debugger breakpoint inside MyWhere

and MyFirst

foreach

and start understanding what happens when you work with iterators using Linq To Objects.

0


source


.Where()

Filters the collection based on expression, it will return a collection of type IEnumerable

, so here you can use it to search for products by name Foo

...

void Main()
{
    List<Product> Products = new List<Product>
    {
        new Product { ProductID = 1, Name = "Foo", Description = "Amazing Foo", Price = 99.99M },
        new Product { ProductID = 2, Name = "Foo", Description = "Not so good Foo", Price = 49.99M },
        new Product { ProductID = 3, Name = "Bar", Description = "Avg Bar", Price = 29.99M } 
    };

    IEnumerable<Product> foos = Products.Where (product => product.Name == "Foo");
}

public class Product
{
    public int ProductID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
}

      

Where where example

If we change this and use it First()

, we get a first instance

product that matches our request, it finds that first instance and completes the request. If it doesn't find a match, it will throw an exception.

Product foo = Products.First (product => product.Name == "Foo");

      

enter image description here

What you are doing is inefficient, the clause Where

will First

iterate over every item in the collection, however the clause will only iterate until it finds a match.

If you are using LINQ to query the database, then this is what the sentence Where

will translate to ...

-- Region Parameters
DECLARE @p0 NVarChar(1000) = 'Foo'
-- EndRegion
SELECT [t0].[ProductID], [t0].[Name], [t0].[Description], [t0].[Price]
FROM [Products] AS [t0]
WHERE [t0].[Name] = @p0

      

And this is what First

will be translated into ..

-- Region Parameters
DECLARE @p0 NVarChar(1000) = 'Foo'
-- EndRegion
SELECT TOP (1) [t0].[ProductID], [t0].[Name], [t0].[Description], [t0].[Price]
FROM [Products] AS [t0]
WHERE [t0].[Name] = @p0

      


When used, the .Where().First()

expression will not iterate over every item in the collection, instead it will just exit after the condition of the expression is metFirst()

-1


source


Law and efficiency are two different things. An exception will be thrown first if there are no items in the programmed sequence. FirstOrDefault won't. Where one can lazily execute or defer until the results are needed, adding a call (for First or FirstOrDefault, etc.) essentially evaluates the query there.

-2


source







All Articles