Both the List.Exists method and IEnumerable.Any method can be used to find the first element that satisfies a predicate
in a collection. However, List.Exists can be faster than IEnumerable.Any for List objects, as well as requires
significantly less memory. For small collections, the performance difference may be negligible, but for large collections, it can be noticeable. The
same applies to ImmutableList and arrays too.
It is important to enable this rule with caution, as performance outcomes can vary significantly across different runtimes. Notably, the performance improvements in .NET 9 have brought
Any closer to the performance of collection-specific Exists methods in most scenarios.
Applies to
We measured at least 3x improvement in execution time. For more details see the Benchmarks section from the More info
tab.
Also, no memory allocations were needed for the Exists method, since the search is done in-place.
Since LINQ to
Entities relies a lot on System.Linq for query conversion,
this rule won’t raise when used within LINQ to Entities syntaxes.
The Exists method is defined on the collection class, and it has the same signature as Any extension method if a
predicate is used. The method can be replaced in place.
bool ContainsEven(List<int> data) =>
data.Any(x => x % 2 == 0);
bool ContainsEven(int[] data) =>
data.Any(x => x % 2 == 0);
bool ContainsEven(List<int> data) =>
data.Exists(x => x % 2 == 0);
bool ContainsEven(int[] data) =>
Array.Exists(data, x => x % 2 == 0);
| Method | Runtime | Categories | Mean | Standard Deviation | Allocated |
|---|---|---|---|---|---|
ArrayAny |
.NET 8.0 |
Array |
1,174.0 ns |
16.44 ns |
32 B |
ArrayExists |
.NET 8.0 |
Array |
570.6 ns |
7.12 ns |
- |
ArrayAny |
.NET 9.0 |
Array |
358.5 ns |
5.57 ns |
- |
ArrayExists |
.NET 9.0 |
Array |
581.6 ns |
6.17 ns |
- |
ArrayAny |
.NET Framework 4.8.1 |
Array |
4,896.0 ns |
102.83 ns |
32 B |
ArrayExists |
.NET Framework 4.8.1 |
Array |
1,649.4 ns |
29.81 ns |
- |
ImmutableListAny |
.NET 8.0 |
ImmutableList<T> |
7,859.3 ns |
91.45 ns |
72 B |
ImmutableListExists |
.NET 8.0 |
ImmutableList<T> |
5,898.1 ns |
81.69 ns |
- |
ImmutableListAny |
.NET 9.0 |
ImmutableList<T> |
7,748.9 ns |
119.10 ns |
72 B |
ImmutableListExists |
.NET 9.0 |
ImmutableList<T> |
5,705.0 ns |
31.53 ns |
- |
ImmutableListAny |
.NET Framework 4.8.1 |
ImmutableList<T> |
45,118.5 ns |
168.72 ns |
72 B |
ImmutableListExists |
.NET Framework 4.8.1 |
ImmutableList<T> |
41,966.0 ns |
631.59 ns |
- |
ListAny |
.NET 8.0 |
List<T> |
1,643.5 ns |
13.09 ns |
40 B |
ListExists |
.NET 8.0 |
List<T> |
726.2 ns |
11.99 ns |
- |
ListAny |
.NET 9.0 |
List<T> |
398.6 ns |
8.20 ns |
- |
ListExists |
.NET 9.0 |
List<T> |
612.4 ns |
18.73 ns |
- |
ListAny |
.NET Framework 4.8.1 |
List<T> |
5,621.5 ns |
35.80 ns |
40 B |
ListExists |
.NET Framework 4.8.1 |
List<T> |
1,748.0 ns |
11.76 ns |
- |
The results were generated by running the following snippet with BenchmarkDotNet:
// Explicitly cache the delegates to avoid allocations inside the benchmark.
private readonly static Func<int, bool> ConditionFunc = static x => x == -1 * Math.Abs(x);
private readonly static Predicate<int> ConditionPredicate = static x => x == -1 * Math.Abs(x);
private List<int> list;
private ImmutableList<int> immutableList;
private int[] array;
[Params(1_000)]
public int N { get; set; }
[GlobalSetup]
public void GlobalSetup()
{
list = Enumerable.Range(0, N).Select(x => N - x).ToList();
immutableList = ImmutableList.CreateRange(list);
array = list.ToArray();
}
[BenchmarkCategory("List<T>"), Benchmark]
public bool ListAny() =>
list.Any(ConditionFunc);
[BenchmarkCategory("List<T>"), Benchmark(Baseline = true)]
public bool ListExists() =>
list.Exists(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>"), Benchmark(Baseline = true)]
public bool ImmutableListAny() =>
immutableList.Any(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>"), Benchmark]
public bool ImmutableListExists() =>
immutableList.Exists(ConditionPredicate);
[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public bool ArrayAny() =>
array.Any(ConditionFunc);
[BenchmarkCategory("Array"), Benchmark]
public bool ArrayExists() =>
Array.Exists(array, ConditionPredicate);
Hardware configuration:
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores [Host] : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256 .NET 8.0 : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET 9.0 : .NET 9.0.0 (9.0.24.47305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256