Производительность 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.
Верны ли мои мысли или что-то не хватает?
Ответы
Ответ 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]