Która metoda działa lepiej:.Any () vs. Count ()> 0?
W przestrzeni nazw System.Linq
możemy teraz rozszerzyć nasze IEnumerable
o Any()
i Count()
metody rozszerzenia .
Ostatnio powiedziano mi, że jeśli chcę sprawdzić, czy Kolekcja zawiera 1 lub więcej elementów wewnątrz niej, powinienem użyć metody rozszerzenia .Any()
zamiast metody rozszerzenia .Count() > 0
, ponieważ metoda rozszerzenia .Count()
musi iterować przez wszystkie elementy.
Po drugie, niektóre zbiory mają właściwość (nie metodę rozszerzenia), która jest Count
lub Length
. Czy lepiej byłoby użyć tych, zamiast .Any()
lub .Count()
?
Yea / nae ?
8 answers
Jeśli zaczynasz od czegoś, co ma .Length
LUB .Count
(np. ICollection<T>
, IList<T>
, List<T>
, etc) - wtedy będzie to najszybsza opcja, ponieważ nie musi przejść przez GetEnumerator()
/MoveNext()
/Dispose()
Sekwencja wymagana przez Any()
do sprawdzenia niepustej sekwencji IEnumerable<T>
.
Dla tylko IEnumerable<T>
, wtedy Any()
będzie ogólnie będzie szybciej, ponieważ wystarczy spojrzeć na jedną iterację. Należy jednak pamiętać, że implementacja LINQ-to-Objects Count()
sprawdza ICollection<T>
(używając .Count
jako optymalizacja) - więc jeśli źródłem danych jest bezpośrednio lista/kolekcja, nie będzie dużej różnicy. Nie pytaj mnie, dlaczego nie używa Nie-generycznego ICollection
...
Oczywiście, jeśli użyłeś LINQ do filtrowania itp. (Where
itd.), będziesz miał sekwencję opartą na blokach iteracyjnych, więc ta optymalizacja ICollection<T>
jest bezużyteczna.
Ogólnie z IEnumerable<T>
: trzymaj się Any()
;- p
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-20 12:51:19
Notatka: napisałem tę odpowiedź, gdy Entity Framework 4 był rzeczywisty. Celem tej odpowiedzi nie było przejście do trywialnych testów wydajności .Any()
vs .Count()
. Chodziło o to, aby zasygnalizować, że EF jest daleki od doskonałości. Nowsze wersje są lepsze... ale jeśli masz część kodu, która jest powolna i używa EF, przetestuj z bezpośrednim TSQL i porównaj wydajność zamiast polegać na założeniach (że .Any()
jest zawsze szybszy niż .Count() > 0
).
Choć zgadzam się z większością głosowanych odpowiedzi i komentarze-szczególnie w punkcie Any
sygnały intencja programisty lepsze niż Count() > 0
- miałem sytuację, w której Licznik jest szybszy o rząd wielkości na serwerze SQL (EntityFramework 4).
Oto zapytanie z Any
że wyjątek timeout Thew (na ~200.000 rekordów):
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
wersja wykonywana w ciągu milisekund:
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();
Muszę znaleźć sposób, aby zobaczyć, co dokładnie SQL oba LINQs produkować - ale to oczywiste, że jest ogromna wydajność różnica między Count
i Any
w niektórych przypadkach, i niestety wydaje się, że nie można po prostu trzymać się Any
we wszystkich przypadkach.
EDIT: tutaj są generowane SQL. Piękności jak widać;)
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
Wydaje się, że pure Where with EXISTS działa znacznie gorzej niż obliczanie Count, a następnie Robienie Where with Count == 0.
Dajcie znać, jeśli zobaczycie jakiś błąd w moich ustaleniach. Co można z tego wszystkiego wyciągnąć niezależnie od Count dyskusja jest taka, że każdy bardziej złożony LINQ jest o wiele lepszy po przepisaniu jako procedura składowana ;).Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-30 18:16:39
Ponieważ jest to dość popularny temat i odpowiedzi różnią się, musiałem spojrzeć na nowy problem.
Testowanie env: EF 6.1.3, SQL Server, 300K records
Model Tabeli :
class TestTable
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
Kod badania:
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();
}
}
}
Wyniki:
Any () ~ 3ms
Count() ~ 230ms dla pierwszego zapytania, ~ 400ms dla drugiego
Uwagi:
W moim przypadku EF nie generował SQL, tak jak @Ben wspomniał w swoim poście.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-28 08:14:57
EDIT: został poprawiony w wersji EF 6.1.1. a ta odpowiedź nie jest już aktualna
Dla SQL Server i EF4-6, Count() działa około dwa razy szybciej niż Any().
Kiedy uruchomisz tabelę.Any(), to wygeneruje coś w rodzaju (alert: nie krzywdź mózgu próbując to zrozumieć )
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]
To wymaga 2 skanów wierszy z twoim stanem.
Nie lubię pisać Count() > 0
ponieważ ukrywa to moją intencję. Wolę używać niestandardowego predykatu dla to:
public static class QueryExtensions
{
public static bool Exists<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
{
return source.Count(predicate) > 0;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-13 21:58:50
Cóż, metoda rozszerzenia .Count()
nie użyje właściwości .Count
, ale zakładam, że nie użyjesz metody .Count()
dla prostej kolekcji, ale raczej na końcu instrukcji LINQ z kryteriami filtrowania itp.
W tym kontekście, .Any()
będzie szybszy niż .Count() > 0
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-09 13:37:46
To zależy, jak duży jest zestaw danych i jakie są Twoje wymagania dotyczące wydajności?
Jeśli to nic wielkiego użyj najbardziej czytelnej formy, który dla mnie jest dowolny, ponieważ jest krótszy i czytelny, a nie równanie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-22 23:43:13
Możesz zrobić prosty test, aby to zrozumieć:
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;
Sprawdź wartości testCount i testAny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-08 15:18:41
O metodzie Count(), Jeśli IEnumerable jest ICollection, to nie możemy iterować we wszystkich elementach, ponieważ możemy pobrać Count pole z ICollection, Jeśli IEnumerable nie jest ICollection musimy iterować wszystkie elementy używając while z MoveNext, spójrz na kod. NET Framework:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
throw Error.ArgumentNull("source");
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null)
return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null)
return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator())
{
checked
{
while (e.MoveNext()) count++;
}
}
return count;
}
Reference: Reference Source Enumerable
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-28 17:35:36