Подтвердить что ты не робот

Производительность LINQ Any vs FirstOrDefault!= Null

В код Open Source Project (OSP), который я вношу, есть несколько мест, где он должен быть определен, если элемент в коллекции удовлетворяет определенному условию.

Я видел использование выражения LINQ Any(lambda expression) в некоторых случаях и FirstOrDefault(lambda expression) != null в других, но никогда не думал об этом.

Теперь я достиг точки, где мне нужно сделать несколько итераций для коллекций, сделанных из запросов, в БД и хочу оптимизировать время выполнения.

Итак, я понял, что FirstOrDefault(lambda expression) != null должен быть быстрее, чем Any(lambda expression), правильно?

В случае FirstOrDefault(lambda expression) != null итерация (возможно) останавливается, когда находит элемент, который удовлетворяет условию (худший сценарий он выполняет итерацию по всей коллекции и возвращает null).

В случае Any(lambda expression) я предполагаю, что итерация продолжается до конца коллекции, даже если найден элемент, который удовлетворяет условию.

Изменить: вышеизложенное неверно, поскольку Джексон Папа упомянул и связал связанную статью MSDN.

Верны ли мои мысли или что-то не хватает?

4b9b3361

Ответ 1

Перечисление в Any() останавливается, как только находит соответствующий элемент:

http://msdn.microsoft.com/en-us/library/bb534972.aspx

Я ожидаю, что производительность будет очень похожа. Обратите внимание, что версия FirstOrDefault не будет работать с коллекцией типов значений (поскольку значение по умолчанию не равно null), но версия Any будет работать.

Ответ 2

Вы смешиваете вещи здесь. Вы говорите о коллекциях, но, похоже, вы не используете LINQ для объектов, а запрашиваете базу данных.

LINQ к объектам:
Enumerable.Any и Enumerable.FirstOrDefault должны выполнить то же самое, потому что их код почти идентичен:

FirstOrDefault:

foreach (TSource source1 in source)
{
    if (predicate(source1))
        return source1;
}
return default (TSource);

Any:

foreach (TSource source1 in source)
{
    if (predicate(source1))
        return true
}
return false;

LINQ к некоторой базе данных:
Вы используете Entity Framework, LINQ to SQL или NHibernate и используете Queryable.Any и Queryable.FirstOrDefault в соответствующем контексте данных.
В этом случае на самом деле нет коллекций, потому что эти вызовы не выполняются в объектах памяти, а переводятся в SQL.

Это означает, что разница в производительности связана с тем, как поставщик LINQ переводит код в SQL, поэтому лучше всего было бы сначала проверить созданные операторы. Они эквивалентны? Или они сильно отличаются (select count(0) from X и select top 1 from X)? Тогда разница может заключаться в оптимизаторе запросов к БД, индексам, а что нет...

Ответ 3

Проблема с этим вопросом заключается в том, что он не задан в контексте. Я предоставляю ответ, потому что я вижу это в обзорах кода, и это беспокоит меня. LINQ не должен служить оправданием, чтобы перестать думать.

var people = new [] { "Steve", "Joe" };

if (people.Any(s => s == "Joe"))
{
    var joe = people.First(s => s == "Joe");
    // do something with joe
}

// if people is 1,000,000 people and joe is near the end do we want BigO to approach 2N here at worst case ?

var joe1N = people.FirstOrDefault(s => s == "Joe");
if (joe1N != null)
{
    // do something with joe
}

// or do we want to ensure worst case is N by simply using a variable ?

Ответ 4

Почему это должно продолжаться после того, как он нашел элемент, удовлетворяющий условию? Если условие применимо к 1 элементу, то оно квалифицируется как "любое".

Я думаю, что они должны работать примерно одинаково, но Any() действительно ясно выражает ваше намерение.

Ответ 5

Мои два цента...

У меня была огромная проблема с производительностью с Any(). Я использую сетку Telerik для отображения списка связанных данных, т.е.

- У меня есть таблица "PEOPLE"

Таблица

-A "COMPANY"

Таблица ссылок

-A "PEOPLE_COMPANY"

Таблица ссылок

-A "PEOPLE_ROL"

-And таблица "ROL" с основной категорией, подкатегорией и описанием.

Представление смешивает данные и имеет несколько свойств, которые загружают данные по запросу о конкретных ролях (admin, репортер, менеджер).

var x = GetPeople().Where(p => p.ROLs.Any(i => i.USO_Id == MainCatId && i.TUP_Id == (int)RolType) && p.PER_Id != per_id);

var x = GetPersonas().Where(p => p.ROLs.FirstOrDefault(i => i.USO_Id == MainCatId && i.TUP_Id == (int)RolType) != null && p.PER_Id != per_id);

Моя сетка использует AJAX и занимает более 10 секунд для загрузки с использованием "Любые" и 3 или менее с использованием "FirstOrDefault". Не нашли времени для его отладки в качестве требовательных для перехвата вызовов от компонентов telerik и моей модели.

Надеюсь, что это поможет... так хорошо протестируйте:)

Ответ 6

Мы можем использовать .Count(x = > x....)!= 0 вместо использования .Any(x = > x....) или .FirstOrDefault( x = > x....)!= null

Поскольку генерация запросов Linq ниже,

(В Any() условие 2 (не существует) не нужно, я думаю.)

. Любой (x = > x.Col_1 == 'xxx')

    (@p__linq__0 varchar(8000))SELECT 
CASE WHEN ( EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[Table_X] AS [Extent1]
    WHERE [Extent1].[Col_1] = @p__linq__0
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[Table_X] AS [Extent2]
    WHERE [Extent2].[Col_1] = @p__linq__0
)) THEN cast(0 as bit) END AS [C1]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]

.FirstOrDefault(x = > x.Col_1 == 'xxx')!= null

 (@p__linq__0 varchar(8000))SELECT TOP (1) 
[Extent1].[Col_1] AS [Col_1], 
[Extent1].[Col_2] AS [Col_2], 
...
[Extent1].[Col_n] AS [Col_n]
FROM [dbo].[Table_X] AS [Extent1]
WHERE [Extent1].[Col_1] = @p__linq__0

.Count(x = > x.Col_1 == 'xxx')!= 0

  (@p__linq__0 varchar(8000))SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Table_X] AS [Extent1]
    WHERE [Extent1].[Col_1] = @p__linq__0
)  AS [GroupBy1]