Какой метод работает лучше:.Any () vs. Count ()> 0?
на System.Linq
пространство имен, теперь мы можем расширить наши IEnumerable
's, чтобы иметьAny()
и Count()
методы расширения.
недавно мне сказали, что если я хочу проверить, что коллекция содержит 1 или более элементов внутри нее, я должен использовать .Any()
метод расширения вместо .Count() > 0
метод расширения, поскольку .Count()
метод расширения должен перебирать все элементы.
во-вторых, некоторые коллекции имеют свойства (не метод расширения), что составляет Count
или Length
. Было бы лучше использовать их, а не .Any()
или .Count()
?
да / нет ?
8 ответов
если вы начинаете с чего-то, что имеет .Length
или .Count
(например,ICollection<T>
, IList<T>
, List<T>
и т. д.) - Тогда это будет самый быстрый вариант, так как ему не нужно проходить через GetEnumerator()
/MoveNext()
/Dispose()
последовательность требуемых Any()
чтобы проверить непустой IEnumerable<T>
последовательности.
только IEnumerable<T>
, потом Any()
будет вообще быть быстрее, так как он должен смотреть только на одну итерацию. Однако обратите внимание, что реализация LINQ-to-Objects Count()
кто ICollection<T>
(через .Count
как оптимизация) - поэтому, если ваш базовый источник данных напрямую список / коллекция, не будет большой разницы. Не спрашивайте меня, почему он не использует non-generic ICollection
...
конечно, если вы использовали LINQ для фильтрации и т. д. (Where
etc), у вас будет последовательность на основе итератора, и поэтому это ICollection<T>
оптимизация-это бесполезно.
в целом IEnumerable<T>
: палка с Any()
- p
Примечание: Я написал этот ответ, когда Entity Framework 4 была актуальной. Смысл этого ответа был не в том, чтобы попасть в тривиальное .Any()
vs .Count()
тестирование производительности. Смысл был в том, чтобы сигнализировать, что EF далек от совершенства. Новые версии лучше... но если у вас есть часть кода, которая медленная, и она использует EF, протестируйте с помощью direct TSQL и сравните производительность, а не полагаясь на предположения (это .Any()
всегда быстрее, чем .Count() > 0
).
хотя я согласен с большинством голосов ответ и комментарии-особенно по пункту Any
сигналы разработчик намерениях лучше, чем Count() > 0
- у меня была ситуация, в которой счетчик быстрее на порядок на SQL Server (EntityFramework 4).
вот запрос Any
это исключение тайм-аута thew (на ~ 200.000 записях):
con = db.Contacts.
Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated
&& !a.NewsletterLogs.Any(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr)
).OrderBy(a => a.ContactId).
Skip(position - 1).
Take(1).FirstOrDefault();
Count
версия выполнена в считанные миллисекунды:
con = db.Contacts.
Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated
&& a.NewsletterLogs.Count(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr) == 0
).OrderBy(a => a.ContactId).
Skip(position - 1).
Take(1).FirstOrDefault();
мне нужно найти способ узнать, какой именно SQL оба LINQs производят - но очевидно, что существует огромная разница в производительности между Count
и Any
в некоторых случаях, и, к сожалению, кажется, вы не можете просто придерживаться Any
во всех случаях.
EDIT: здесь генерируются SQLs. Красавицы, Как видите;)
ANY
:
exec sp_executesql N'SELECT TOP (1) [Project2].[ContactId] AS [ContactId], [Project2].[CompanyId] AS [CompanyId], [Project2].[ContactName] AS [ContactName], [Project2].[FullName] AS [FullName], [Project2].[ContactStatusId] AS [ContactStatusId], [Project2].[Created] AS [Created] FROM ( SELECT [Project2].[ContactId] AS [ContactId], [Project2].[CompanyId] AS [CompanyId], [Project2].[ContactName] AS [ContactName], [Project2].[FullName] AS [FullName], [Project2].[ContactStatusId] AS [ContactStatusId], [Project2].[Created] AS [Created], row_number() OVER (ORDER BY [Project2].[ContactId] ASC) AS [row_number] FROM ( SELECT [Extent1].[ContactId] AS [ContactId], [Extent1].[CompanyId] AS [CompanyId], [Extent1].[ContactName] AS [ContactName], [Extent1].[FullName] AS [FullName], [Extent1].[ContactStatusId] AS [ContactStatusId], [Extent1].[Created] AS [Created] FROM [dbo].[Contact] AS [Extent1] WHERE ([Extent1].[CompanyId] = @p__linq__0) AND ([Extent1].[ContactStatusId] <= 3) AND ( NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[NewsletterLog] AS [Extent2] WHERE ([Extent1].[ContactId] = [Extent2].[ContactId]) AND (6 = [Extent2].[NewsletterLogTypeId]) )) ) AS [Project2] ) AS [Project2] WHERE [Project2].[row_number] > 99 ORDER BY [Project2].[ContactId] ASC',N'@p__linq__0 int',@p__linq__0=4
COUNT
:
exec sp_executesql N'SELECT TOP (1) [Project2].[ContactId] AS [ContactId], [Project2].[CompanyId] AS [CompanyId], [Project2].[ContactName] AS [ContactName], [Project2].[FullName] AS [FullName], [Project2].[ContactStatusId] AS [ContactStatusId], [Project2].[Created] AS [Created] FROM ( SELECT [Project2].[ContactId] AS [ContactId], [Project2].[CompanyId] AS [CompanyId], [Project2].[ContactName] AS [ContactName], [Project2].[FullName] AS [FullName], [Project2].[ContactStatusId] AS [ContactStatusId], [Project2].[Created] AS [Created], row_number() OVER (ORDER BY [Project2].[ContactId] ASC) AS [row_number] FROM ( SELECT [Project1].[ContactId] AS [ContactId], [Project1].[CompanyId] AS [CompanyId], [Project1].[ContactName] AS [ContactName], [Project1].[FullName] AS [FullName], [Project1].[ContactStatusId] AS [ContactStatusId], [Project1].[Created] AS [Created] FROM ( SELECT [Extent1].[ContactId] AS [ContactId], [Extent1].[CompanyId] AS [CompanyId], [Extent1].[ContactName] AS [ContactName], [Extent1].[FullName] AS [FullName], [Extent1].[ContactStatusId] AS [ContactStatusId], [Extent1].[Created] AS [Created], (SELECT COUNT(1) AS [A1] FROM [dbo].[NewsletterLog] AS [Extent2] WHERE ([Extent1].[ContactId] = [Extent2].[ContactId]) AND (6 = [Extent2].[NewsletterLogTypeId])) AS [C1] FROM [dbo].[Contact] AS [Extent1] ) AS [Project1] WHERE ([Project1].[CompanyId] = @p__linq__0) AND ([Project1].[ContactStatusId] <= 3) AND (0 = [Project1].[C1]) ) AS [Project2] ) AS [Project2] WHERE [Project2].[row_number] > 99 ORDER BY [Project2].[ContactId] ASC',N'@p__linq__0 int',@p__linq__0=4
кажется, что pure Where with EXISTS работает намного хуже, чем вычисление количества, а затем делает, где с Count == 0.
Дайте мне знать, если вы видите какие-то ошибки в моих выводах. Что можно извлечь из всего этого независимо от любого обсуждения vs Count, так это то, что любой более сложный LINQ лучше переписывается как хранимая процедура ;).
поскольку это довольно популярная тема и ответы отличаются, мне пришлось по-новому взглянуть на проблему.
тестирование env: EF 6.1.3, SQL Server, 300k records
таблица модель:
class TestTable
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
тестовый код:
class Program
{
static void Main()
{
using (var context = new TestContext())
{
context.Database.Log = Console.WriteLine;
context.TestTables.Where(x => x.Surname.Contains("Surname")).Any(x => x.Id > 1000);
context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Any(x => x.Id > 1000);
context.TestTables.Where(x => x.Surname.Contains("Surname")).Count(x => x.Id > 1000);
context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Count(x => x.Id > 1000);
Console.ReadLine();
}
}
}
результаты:
Any () ~ 3ms
Count () ~ 230ms для первого запроса, ~ 400ms для второго
Примечания:
для мой случай EF не генерировал SQL, как @Ben, упомянутый в его сообщении.
EDIT: Это было исправлено в EF версии 6.1.1. и этот ответ уже не актуален
для SQL Server и EF4-6 Count () работает примерно в два раза быстрее, чем Any ().
при запуске таблицы.Любой(), он будет генерировать что-то вроде(предупреждение: не повредите мозг, пытаясь понять его)
SELECT
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [Table] AS [Extent1]
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT
1 AS [C1]
FROM [Table] AS [Extent2]
)) THEN cast(0 as bit) END AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
для этого требуется 2 сканирования строк с вашим состоянием.
Я не люблю писать Count() > 0
потому что он скрывает мои намерение. Я предпочитаю использовать пользовательский предикат для этого:
public static class QueryExtensions
{
public static bool Exists<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
{
return source.Count(predicate) > 0;
}
}
Ну .Count()
метод расширения не используйте .Count
свойство, но я бы предположил, что вы не будете использовать .Count()
метод для простой коллекции, но скорее в конце оператора LINQ с критериями фильтрации и т. д.
в этой связи .Any()
будет быстрее, чем .Count() > 0
.
Это зависит от того, насколько большой набор данных и каковы ваши требования?
Если это ничего гигантского, используйте наиболее читаемую форму, который для меня любой, потому что он короче и читаем, а не уравнение.
вы можете сделать простой тест, чтобы выяснить это:
var query = //make any query here
var timeCount = new Stopwatch();
timeCount.Start();
if (query.Count > 0)
{
}
timeCount.Stop();
var testCount = timeCount.Elapsed;
var timeAny = new Stopwatch();
timeAny.Start();
if (query.Any())
{
}
timeAny.Stop();
var testAny = timeAny.Elapsed;
Проверьте значения testCount и testAny.
о Count () способ, если IEnumarable - это ICollection, тогда мы не можем перебирать все элементы, потому что мы можем получить графа