
org.sonar.plugins.vbnet.S6602.html Maven / Gradle / Ivy
Why is this an issue?
Both the List.Find
method and the Enumerable.FirstOrDefault
method can be used to locate the first element that meets a
specified condition within a collection. However, for List
objects, List.Find
may offer superior performance compared to
Enumerable.FirstOrDefault
. While the performance difference might be negligible for small collections, it can become significant for
larger collections. This observation also holds true for ImmutableList
and arrays.
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
FirstOrDefault
closer to the performance of collection-specific Find
methods in most scenarios.
Applies to
What is the potential impact?
We measured at least 2x improvement in the execution time. For more details see the Benchmarks
section from the More info
tab.
How to fix it
The Find
method is defined on the collection class, and it has the same signature as FirstOrDefault
extension method. The
function can be replaced in place.
Code examples
Noncompliant code example
Function GetValue(data As List(Of Integer)) As Integer
Return data.FirstOrDefault(Function(x) x Mod 2 = 0)
End Function
Function GetValue(data() As Integer) As Integer
Return data.FirstOrDefault(Function(x) x Mod 2 = 0)
End Function
Compliant solution
Function GetValue(data As List(Of Integer)) As Integer
Return data.Find(Function(x) x Mod 2 = 0)
End Function
Function GetValue(data() As Integer) As Integer
Return Array.Find(data, Function(x) x Mod 2 = 0)
End Function
Resources
Documentation
- List<T>.Find(Predicate<T>)
- Array.Find<T>(T[], Predicate<T>)
- ImmutableList<T>.Find(Predicate<T>)
- Enumerable.FirstOrDefault(Predicate<T>)
Benchmarks
Method
Runtime
Categories
Mean
Standard Deviation
Allocated
ArrayFirstOrDefault
.NET 8.0
Array
10.515 μs
0.1410 μs
32 B
ArrayFind
.NET 8.0
Array
4.417 μs
0.0729 μs
-
ArrayFirstOrDefault
.NET 9.0
Array
2.262 μs
0.0135 μs
-
ArrayFind
.NET 9.0
Array
3.428 μs
0.0206 μs
-
ArrayFirstOrDefault
.NET Framework 4.8.1
Array
45.074 μs
0.7517 μs
32 B
ArrayFind
.NET Framework 4.8.1
Array
13.948 μs
0.1496 μs
-
ImmutableListFirstOrDefault
.NET 8.0
ImmutableList<T>
83.796 μs
1.3199 μs
72 B
ImmutableListFind
.NET 8.0
ImmutableList<T>
59.720 μs
1.0723 μs
-
ImmutableListFirstOrDefault
.NET 9.0
ImmutableList<T>
81.984 μs
1.0886 μs
72 B
ImmutableListFind
.NET 9.0
ImmutableList<T>
58.288 μs
0.8079 μs
-
ImmutableListFirstOrDefault
.NET Framework 4.8.1
ImmutableList<T>
446.893 μs
9.8430 μs
76 B
ImmutableListFind
.NET Framework 4.8.1
ImmutableList<T>
427.476 μs
3.3371 μs
-
ListFirstOrDefault
.NET 8.0
List<T>
14.808 μs
0.1723 μs
40 B
ListFind
.NET 8.0
List<T>
6.040 μs
0.1104 μs
-
ListFirstOrDefault
.NET 9.0
List<T>
2.233 μs
0.0154 μs
-
ListFind
.NET 9.0
List<T>
4.458 μs
0.0745 μs
-
ListFirstOrDefault
.NET Framework 4.8.1
List<T>
57.290 μs
1.0494 μs
40 B
ListFind
.NET Framework 4.8.1
List<T>
18.476 μs
0.0504 μs
-
Glossary
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;
private readonly static Predicate<int> ConditionPredicate = static x => x == 1;
private List<int> list;
private ImmutableList<int> immutableList;
private int[] array;
public const int N = 10_000;
[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(Baseline = true)]
public int ListFirstOrDefault() =>
list.FirstOrDefault(ConditionFunc);
[BenchmarkCategory("List<T>"), Benchmark]
public int ListFind() =>
list.Find(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>"), Benchmark(Baseline = true)]
public int ImmutableListFirstOrDefault() =>
immutableList.FirstOrDefault(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>"), Benchmark]
public int ImmutableListFind() =>
immutableList.Find(ConditionPredicate);
[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public int ArrayFirstOrDefault() =>
array.FirstOrDefault(ConditionFunc);
[BenchmarkCategory("Array"), Benchmark]
public int ArrayFind() =>
Array.Find(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